/*!! uuAltCSS.js { version: "0.2", license: "MIT", author: "uupaa.js@gmail.com" } */

// === uuMeta ===
// depend: none
/*
window.UUMETA_DEBUG = 0;
window.UUMETA_IMAGE_DIR = "."
uuMeta.ie
uuMeta.iemode8
uuMeta.quirks
uuMeta.opera
uuMeta.gecko
uuMeta.gecko191 - Firefox3.5+
uuMeta.firefox
uuMeta.webkit
uuMeta.webkit522 - Safari3+, Chrome1+
uuMeta.webkit530 - Safari4+, Chrome2+
uuMeta.safari
uuMeta.chrome
uuMeta.iphone
uuMeta.uaver
uuMeta.slver
uuMeta.flashver
uuMeta.enginever
uuMeta.canvas
uuMeta.runstyle
uuMeta.imagedir
uuMeta.blackout
uuMeta.debug
uuMeta.hex
uuMeta.mix(base, flavor, aroma = undef, override = true) - return base
uuMeta.toArray(fake) - return array
-- element unique id ---
uuMeta.uid(node) - return unique id
uuMeta.uid2node(uid) - return node or void 0
uuMeta.uid2node.refHash() - return { uid: node, ... }
--- boot ---
window.boot - user defined boot loader
uuMeta.boot(callback, highPriority = false)
--- event ---
uuMeta.event.bind(elm, eventName, fn, capture)
uuMeta.event.unbind(elm, eventName, fn, capture)
uuMeta.event.stop(evt)
 */
(function() {
var _meta,  // inner namespace
    _win = window,
    _doc = document,
    _debug = (_win.UUMETA_DEBUG || 0),
    _float = parseFloat,
    _nu = navigator.userAgent,
    _ie = !!_doc.uniqueID,
    _opera = !!_win.opera,
    _gecko = _nu.indexOf("Gecko/") > 0,
    _webkit = _nu.indexOf("WebKit") > 0,
    _chrome = _nu.indexOf("Chrome") > 0,
    _uaver = _opera ? (opera.version().replace(/\d$/, "") - 0) // Opera10 shock
                    : _float((/(?:IE |fox\/|ome\/|ion\/)(\d+\.\d)/.
                             exec(_nu) || [,0])[1]),
    _slver = (function() {
      try {
        var a = ["1.0", "2.0", "3.0"], i = 3, ok,
            o = _ie ? new ActiveXObject("AgControl.AgControl")
                    : navigator.plugins["Silverlight Plug-In"];
        while (i--) {
          ok = _ie ? o.IsVersionSupported(a[i])
                   : _float(/\d+\.\d+/.exec(o.description)[0]) >= _float(a[i]);
          if (ok) {
            return _float(a[i]);
          }
        }
      } catch(err) {}
      return 0;
    })(),
    _flashver = (function() {
      try {
        var o = _ie ? new ActiveXObject("ShockwaveFlash.ShockwaveFlash")
                    : navigator.plugins["Shockwave Flash"],
            v = _ie ? o.GetVariable("$version").replace(/,/g, ".")
                    : o.description,
            m = /\d+\.\d+/.exec(v);
        return m ? _float(m[0], 10) : 0;
      } catch(err) {}
      return 0;
    })(),
    _egver = _float(((/(?:rv\:|ari\/|sto\/)(\d+\.\d+(\.\d+)?)/.
      exec(_nu) || [,0])[1]).toString().
      replace(/[^\d\.]/g, "").replace(/^(\d+\.\d+)(\.(\d+))?$/, "$1$3")
    ),
    _hex = (function() {
      var r = [], i = 256;
      for(; i < 512; ++i) {
        r.push(i.toString(16).slice(1));
      }
      return r;
    })(),
    _mix = function(base,       // @param Hash: mixin base
                    flavor,     // @param Hash: add flavor
                    aroma,      // @param Hash(= undefined): add aroma
                    override) { // @param Boolean(= true): true is override
                                // @return Hash: base
      var i, ride = (override === void 0) || override;

      for (i in flavor) {
        if (ride || !(i in base)) {
          base[i] = flavor[i];
        }
      }
      return aroma ? _meta.mix(base, aroma, void 0, ride) : base;
    },
    _toArray = function(fake) { // @param NodeList/Array:
                                // @return Array:
      if (!_ie && (_opera && _uaver >= 9.5)) {
        return Array.prototype.slice.call(fake, 0);
      }
      var rv = [], ri = -1, i = 0, iz = fake.length;
      for (; i < iz; ++i) {
        rv[++ri] = fake[i];
      }
      return rv;
    },
    _uid2node = {}, // { uid: node, ... }
    _bootFire = 0, // fired flag
    _bootOrder = [[], []], // [high, low]
    UNIQUEID = "uuqid";

_meta = {
  ie:         _ie,
  iemode8:    _ie && _doc.documentMode >= 8,
  quirks:     (_doc.compatMode || "") !== "CSS1Compat",
  opera:      _opera,
  gecko:      _gecko,
  gecko191:   (_gecko && _egver >= 1.91), // Firefox3.5+
  firefox:    _nu.indexOf("Firefox/") > 0,
  webkit:     _webkit,
  webkit522:  (_webkit && _egver >= 522), // Safari3+, Chrome1+
  webkit530:  (_webkit && _egver >= 530), // Safari4+, Chrome2+
  chrome:     _chrome,
  safari:     !_chrome && _nu.indexOf("Safari") > 0,
  iphone:     _webkit && /iPod|iPhone/.test(_nu),
  uaver:      _uaver,     // User Agent version
  slver:      _slver,     // Silverlight version
  flashver:   _flashver,  // Flash version(ver 7 later)
  enginever:  _egver,     // Render engine version
  canvas:     (_ie && _uaver >= 6) || // <canvas> support
              (_opera  && _uaver >= 9.2) ||
              (_gecko  && _egver >= 1.81) ||
              (_webkit && _egver >= 522),
  runstyle:   _ie ? "currentStyle"
                  : (_win.getComputedStyle ||
                     _doc.defaultView.getComputedStyle), // old WebKit
  imagedir:   (_win.UUMETA_IMAGE_DIR || ".").replace(/\/+$/, ""),
  blackout:   0, // 1: fire location.reload()
  debug:      _debug,
  hex:        _hex,
  mix:        _mix,
  toArray:    _toArray
};

// uuMeta.uid - get unique id by node
_meta.uid = function(node) { // @param Node:
                             // @return Number: unique id, from 1
  var newid;

  return node[UNIQUEID] ||
            (_uid2node[node[UNIQUEID] = newid = ++_win[UNIQUEID]] = node,
             newid);
};

// uuMeta.uid2node - get node by unique id
_meta.uid2node = function(uid) { // @param String: unique id
                                 // @return Node/undefined:
  return _uid2node[uid];
};

// uuMeta.uid2node.refHash
_meta.uid2node.refHash = function() { // @return Hash:
  return _uid2node;
};

// uuMeta.boot - catch DOMContentReady/window.onload event
_meta.boot = function(callback,       // @param Function:
                      highPriority) { // @param Boolean(= false):
  (_bootFire && !_meta.blackout)
      ? callback(0x101) // run now
      : _bootOrder[highPriority ? 0 : 1].push(callback); // stock
};

// uuMeta.event
_meta.event = {
  // uuMeta.event.bind
  bind: function(elm,       // @param Node:
                 eventName, // @param String:
                 callback,  // @param Function: callback
                 capture) { // @param Boolean(= false):
    _ie ? elm.attachEvent("on" + eventName, callback)
        : elm.addEventListener(eventName, callback, capture || false);
  },
  // uuMeta.event.unbind
  unbind: function(elm,       // @param Node:
                   eventName, // @param String:
                   callback,  // @param Function: callback
                   capture) { // @param Boolean(= false):
    _ie ? elm.detachEvent("on" + eventName, callback)
        : elm.removeEventListener(eventName, callback, capture || false);
  },
  // uuMeta.event.stop
  stop: function(evt) { // @param EventObject(= undefined):
    evt = evt || _win.event;
    _ie ? (evt.cancelBubble = true) : evt.stopPropagation();
    _ie ? (evt.returnValue = false) : evt.preventDefault();
  }
};

function bootFire(v) {
  if (!_bootFire++) {
    if (_debug) {
      while ( (v = _bootOrder[0].shift()) ) { !_meta.blackout && v(); }
      while ( (v = _bootOrder[1].shift()) ) { !_meta.blackout && v(); }
      !_meta.blackout &&
          (typeof _win.boot === "function") && _win.boot(0x100);
    } else {
      try {
        // high priority
        while ( (v = _bootOrder[0].shift()) ) { !_meta.blackout && v(); }
        // low priority
        while ( (v = _bootOrder[1].shift()) ) { !_meta.blackout && v(); }
        // boot loader
        !_meta.blackout &&
            (typeof _win.boot === "function") && _win.boot(0x100);
      } catch(err) {}
    }
  }
}

// --- initialize ---
// boot: windowReadyState and domReadyState
if (_ie) {
  _win.attachEvent("onload", bootFire);
  (function peek() {
    try {
      _doc.firstChild.doScroll("up"), bootFire();
    } catch(err) { setTimeout(peek, 16); }
  })();
} else { // WebKit, Firefox, Opera
  _win.addEventListener("load", bootFire, false);
  _doc.addEventListener("DOMContentLoaded", bootFire, false);
}

// prebuild element unique id ( node[UNIQUEID] )
_meta.boot(function() {
  var node = _doc.getElementsByTagName("*"), v, i = 0, iz = node.length,
      newid;

  for (; i < iz; ++i) {
    v = node[i];
    if (!_ie || v.nodeType === 1) {
      v[UNIQUEID] ||
          (_uid2node[v[UNIQUEID] = newid = ++_win[UNIQUEID]] = v, newid);
    }
  }
}, 1);

// init VML namespace
_ie && _meta.boot(function() {
  var NS = 'urn:schemas-microsoft-com:', NSV = '#default#VML',
      ns = _doc.namespaces;

  if (!ns["v"]) {
    ns.add("v", NS + "vml", NSV);
    ns.add("o", NS + "office:office", NSV);
  }
  _doc.createStyleSheet().cssText =
    "canvas{display:inline-block;text-align:left;width:300px;height:150px}" +
    "v\:roundrect,v\:oval,v\:shape,v\:stroke,v\:fill,v\:textpath," +
    "v\:image,v\:line,v\:skew,v\:path,o\:opacity2" +
    "{behavior:url(" + NSV + ");display:inline-block}"; // inline-block [!]
});

// --- export ---
_win.uuMeta = _meta; // window.uuMeta
_win[UNIQUEID] = 0;  // window.uuqid

})(); // uuMeta scope

// === uuMeta.cdsl - conditional selector ===
// depend: uuMeta
/*
window.UUMETA_DISABLE_CDSL = 1
uuMeta.cdsl()
 */
// --- conditional selector ---
//  <style>
//    div>ul { color: black }                /* for Generic browser */
//    html.ifwebkit div>ul { color: blue }   /* for Safari, Chrome */
//    html.ifchrome3 div>ul { color: green } /* for Google Chrome3 */
//    html.ifopera92 div>ul { color: red }   /* for Opera9.27 */
//    html.ifswlt800.ifshlt600 div>ul { font-size: large }
//                                           /* screen width  < 800 and
//                                              screen height < 600 */
//  </style>
//  +----------------+---------------+------------------------------
//  | CONDITION      | IDENT         | NOTE
//  +----------------+---------------+------------------------------
//  | IE             | "ifie"        | version >= 6
//  | IE6.0          | "ifie6"       |
//  | IE7.0          | "ifie7"       |
//  | IE8.0          | "ifie8"       |
//  | Opera          | "ifopera"     | version >= 9.20
//  | Opera9.27      | "ifopera92"   |
//  | Opera9.63      | "ifopera96"   |
//  | Opera10.00     | "ifopera10"   |
//  | Opera10.10     | "ifopera101"  |
//  | Gecko          | "ifgecko"     |
//  | Gecko1.9.1     | "ifgecko191b" | Gecko engine version >= 191(Firefox3.5)
//  | Firefox        | "iffx"        | version >= 2
//  | Firefox2.0     | "iffx2"       |
//  | Firefox3.0     | "iffx3"       |
//  | Firefox3.5     | "iffx35"      |
//  | WebKit         | "ifwebkit"    | has Safari, Chrome, iPhone
//  | WebKit522      | "ifwebkit522b"| WebKit engine version >= 522(Safari3)
//  | WebKit530      | "ifwebkit530b"| WebKit engine version >= 530(Safari4)
//  | Safari         | "ifsafari"    | version >= 3
//  | Safari3.2.3    | "ifsafari32"  |
//  | Safari4.0      | "ifsafari4"   |
//  | iPod           | "ifiphone"    |
//  | iPhone         | "ifiphone"    |
//  | Chrome         | "ifchrome"    | version >= 1
//  | Chrome1.0      | "ifchrome1"   |
//  | Chrome2.0      | "ifchrome2"   |
//  | Chrome3.0      | "ifchrome3"   |
//  | Silverlight    | "ifsl"        | version >= 1
//  | Flash          | "ifflash"     | version >= 7
//  | HTML5::Canvas  | "ifcanvas"    | enable HTML5::Canvas
//  | JavaScript     | "ifjs"        | enable JavaScript
//  | width  <  800  | "ifw800s"     | screen width  <  800px
//  | width  >= 1200 | "ifw1200b"    | screen width  >= 1200px
//  | height <  600  | "ifh600s"     | screen height <  600px
//  | height >= 1000 | "ifh1000b"    | screen height >= 1000px
//  +----------------+---------------+------------------------------
(function() {
var _cdsl, // inner namespace
    _mm = uuMeta,
    _uaver = _mm.uaver,
    _egver = _mm.enginever;

_cdsl = function() {
  var _root = document.getElementsByTagName("html")[0],
      cn = [_root.className.replace(/ifnojs/, ""), "ifjs"],
      sw = screen.width, sh = screen.height;

  _mm.ie       && cn.push("ifie ifie" + _uaver);
  _mm.opera    && cn.push("ifopera ifopera" + _uaver);
  _mm.gecko    && cn.push("ifgecko");
  _mm.gecko    && _egver >= 1.91 && cn.push("ifgecko191b");
  _mm.firefox  && cn.push("iffx iffx" + _uaver);
  _mm.webkit   && cn.push("ifwebkit");
  _mm.webkit   && _egver >= 522  && cn.push("ifwebkit522b");
  _mm.webkit   && _egver >= 530  && cn.push("ifwebkit530b");
  _mm.safari   && cn.push("ifsafari ifsafari" + _uaver);
  _mm.chrome   && cn.push("ifchrome ifchrome" + _uaver);
  _mm.iphone   && cn.push("ifiphone");
  _mm.slver    && cn.push("ifsl");
  _mm.flashver && cn.push("ifflash");
  _mm.canvas   && cn.push("ifcanvas");
  (sw <   800) && cn.push("ifw800s");
  (sw >= 1200) && cn.push("ifw1200b");
  (sh <   600) && cn.push("ifh600s");
  (sh >= 1000) && cn.push("ifh1000b");

  if (!window.UUMETA_DISABLE_CDSL) {
    _root.className = cn.join(" ").replace(/^\s+|\s+$/g, ""). // trim
                                   replace(/\./g, "");
  }
};

// --- initialize ---

// --- export ---
uuMeta.cdsl = _cdsl;

})(); // uuMeta.cdsl scope


// === uuMeta.data ===
// depend: uuMeta
/*
uuMeta.data(node, key) - return value
uuMeta.data.bond(node, key, value)
uuMeta.data.unbond(node, key = "")
uuMeta.data.keys(node) - return ["key", ...]
uuMeta.data.nodes(key) - return [node, ...]
 */
(function() {
var _data, // inner namespace
    _mm = uuMeta,
    _uiddb = {}, // { uid: node, ... }
    _keydb = {}; // { key: {uid: 1, ...}, ... }

// uuMeta.data - get bonded data
_data = function(node,  // @param Node:
                 key) { // @param String: keyword
                        // @return Mix/undefined: bonded value
  return ("bond" in node) ? node.bond[key] : void 0;
};

// uuMeta.data.bond - bond key,value pair for element
_data.bond = function(node,    // @param Node:
                      key,     // @param String: keyword
                      value) { // @param Mix: value
                               // @return Mix: value
  var uid = _mm.uid(node);

  !("bond" in node) && (node.bond = {});
  node.bond[key] = value;

  !(uid in _uiddb) && (_uiddb[uid] = node);
  !(key in _keydb) && (_keydb[key] = {});
  _keydb[key][uid] = 1;
};

// uuMeta.data.unbond
_data.unbond = function(node,  // @param Node:
                        key) { // @param String(= ""): keyword
                               //        "" is unbond all keywords
  var uid = _mm.uid(node), i = 0, ary;

  if ("bond" in node) {
    key ? (delete node.bond[key])
        : (delete node.bond); // all
  }
  ary = key ? [key] : _data.keys(node);
  while ( (key = ary[i++]) ) {
    if (uid in _keydb[key]) {
      delete _keydb[key][uid];
    }
  }
};

// uuMeta.data.keys - find node bonded keywords
_data.keys = function(node) { // @param Node:
                              // @return StringArray: ["key", ...]
  var uid = _mm.uid(node), key, rv = [];

  if (uid in _uiddb) {
    for (key in _keydb) {
      if (uid in key) {
        rv.push(key);
      }
    }
  }
  return rv;
};

// uuMeta.data.nodes - find keyword bonded nodes
_data.nodes = function(key) { // @param String: keyword
                              // @return NodeArray: [node, ...]
  var uid, rv = [];

  if (key in _keydb) {
    for (uid in _keydb[key]) {
      rv.push(_uiddb[uid]);
    }
  }
  return rv;
};

// --- initialize ---

// --- export ---
window.uuMeta.data = _data;

})();

// === uuMeta.event.resize ===
// depend: uuMeta
/*
uuMeta.event.resize(callback, type = false) - return atom
 */
(function() {
var _mm = uuMeta,
    _win = window,
    _doc = document,
    _ie = _mm.ie,
    _ieroot = _mm.quirks ? "body" : "documentElement",
    _db1 = { delay: 40, lock: 0, kill: 0, callback: [] },
    _db2 = { delay: 100, lock: 0, kill: 0, callback: [] };

function getInnerSize() {
  var root = _ie ? _doc[_ieroot] : _win;
  return { w: root.innerWidth  || root.clientWidth,
           h: root.innerHeight || root.clientHeight };
}

// event handler
function type1(callback) { // @param Function/0: callback function or zero
                           // @return Number: callback function atom
  if (!callback) {
    _db1.kill = 1;
    return 0;
  }
  !_db1.callback.length && _mm.event.bind(_win, "resize", ontype1); // init
  _db1.callback.push(callback); // regist
  return _db1.length - 1; // return atom
}

function ontype1() {
  if (_db1.kill) {
    _mm.event.unbind(_win, "resize", ontype1);
    _db1.kill = _db1.lock = 0;
    _db1.callback = []; // clear
    return;
  }
  if (!_db1.lock++) {
    setTimeout(function() {
      var i = 0, iz = _db1.callback.length;
      for (; i < iz; ++i) {
        (i in _db1.callback) && _db1.callback[i]();
      }
      setTimeout(function() { _db1.lock = 0; }, 0); // delay unlock
    }, _db1.delay);
  }
}

// resize agent
function type2(callback) { // @param Function/0: callback function or zero
                           // @return Number: callback function atom
  if (!callback) {
    _db2.kill = 1;
    return 0;
  }
  !_db2.callback.length && (_db2.size = getInnerSize()); // init
  _db2.callback.push(callback); // regist

  function loop() {
    if (_db2.kill) {
      _db2.kill = _db2.lock = 0;
      _db2.callback = []; // clear
      return;
    }
    if (!_db2.lock++) {
      var dim = getInnerSize(), i, iz;
      if (_db2.size.w !== dim.w || _db2.size.h !== dim.h) { // resized
        _db2.size = dim;
        for (i = 0, iz = _db2.callback.length; i < iz; ++i) {
          (i in _db2.callback) && _db2.callback[i]();
        }
      }
      setTimeout(function() { _db2.lock = 0; }, 0); // delay unlock
    }
    setTimeout(loop, _db2.delay);
  }
  setTimeout(loop, _db2.delay);

  return _db2.length - 1; // return atom
}

// --- initialize ---

// --- export ---
_mm.event.resize =
    function(callback, // @param Function/0: callback function or zero
             agent) {  // @param Boolean(= false): use resize agent
  return agent ? type2(callback)  // use resize agent
               : type1(callback); // use event handler
};

})();


// === uuMeta.normalize ===
// depend: uuMeta
/*
uuMeta.normalize.style("css-prop") - return "cssProp"
uuMeta.normalize.attr("for") - return "htmlFor"
 */
(function() {
var _mm = uuMeta,
    STYLE,
    ATTR;

_mm.normalize = {
  // uuMeta.normalize.style - style property normalize
  //    normalize.style("-uu-box-shadow") -> "-uu-box-shadow"
  //    normalize.style("background-color") -> "backgroundColor"
  //    normalize.style("float") -> "cssFloat" or "styleFloat"(IE)
  style: function(name) { // @param String: "css-prop"
                          // @return String: "cssProp"
    if (name in STYLE) { return STYLE[name]; }
    if (!name.indexOf("-")) { return name; } // -webkit-xxx

    return name.replace(/-([a-z])/g, function(m, c) {
      return c.toUpperCase(); // camelize
    });
  },

  // uuMeta.normalize.attr - attribute name normalize
  //    normalize.attr("for") -> "htmlFor"
  attr: function(name) { // @param String: "for"
                         // @return String: "htmlFor"
    return ATTR[name] || name;
  }
};

// --- initialize ---
if (!_mm.ie || _mm.iemode8) { // IE8, other
  STYLE = {
    "float":    "cssFloat",
    styleFloat: "cssFloat"
  };
  ATTR = {
    "for":      "htmlFor"
  };
} else { // IE6, IE7
  STYLE = {
    "float":    "styleFloat",
    cssFloat:   "styleFloat"
  };
  ATTR = {
    "for":      "htmlFor",
    colspan:    "colSpan",
    rowspan:    "rowSpan",
    accesskey:  "accessKey",
    tabindex:   "tabIndex"
  };
}

// --- export ---

})(); // uuMeta.normalize scope


// === uuToken ===
// depend: none
/*
uuToken.split(expr, splitter = " ", notrim = 0) - return ["token", ...]
 */
(function() {
var _token, // inner namespace
    TRIM = /^\s+|\s+$/g;

_token = {
  // uuToken.split - split token
  split: function(expr,     // @param String: expression
                  splitter, // @param String(= " "):
                  notrim) { // @param Boolean(= false): trim space
                            // @return Array: ["token", ...]
    splitter = splitter || " ";
    if (expr.indexOf(splitter) < 0) { return [expr]; }

    var rv = [], ary = expr.split(""), v, w, i = 0,
        nest = 0, quote = 0, q, tmp = [], ti = -1, esc = 0,
        hash = { "(": 2, ")": 3, '"': 4, "'": 4, "\\": 5 };

    hash[splitter] = 1;

    while ( (v = ary[i++]) ) {
      if (esc) {
        esc = 0;
        tmp[++ti] = v;
      } else {
        switch (hash[v] || 0) {
        case 0: tmp[++ti] = v;
                break;
        case 1: if (!quote) {
                  if (nest) {
                    tmp[++ti] = v;
                  } else {
                    w = tmp.join(""),
                    rv.push(notrim ? w : w.replace(TRIM, ""));
                    tmp = [];
                    ti = -1;
                  }
                } else {
                  tmp[++ti] = v;
                }
                break;
        case 2: tmp[++ti] = v;
                !quote && ++nest;
                break;
        case 3: tmp[++ti] = v;
                !quote && --nest;
                break;
        case 4: if (!quote) {
                  quote = 1;
                  q = v;
                } else if (v === q) {
                  quote = 0;
                }
                tmp[++ti] = v;
                break;
        case 5: esc = 1;
                tmp[++ti] = v;
        }
      }
    }
    // remain
    if (tmp.length) {
      w = tmp.join("");
      rv.push(notrim ? w : w.replace(TRIM, ""));
    }
    return rv;
  }
};

// --- initialize ---

// --- export ---
window.uuToken = _token;

})();


// === uuImage ===
// depend: none
/*
uuImage.load(url, callback)
uuImage.getActualDimension(image) - return { w, h }
 */

(function() {
var _image, // inner namespace
    _ie = document.uniqueID;

_image = {
  // uuImage.load - delay loader
  load: function(url,        // @param String:
                 callback) { // @param Function: callback(img, state, dim)
                             //     img: image object
                             //     state: 0(loading...), 1(loaded), -1(error)
                             //     dim: { w, h }
    var img = new Image();
    img.state = 0; // bond

    img.clear = function() { // bond
      img.onerror = "";
      img.onload = "";
      img = void 0; // fix memory leak in IE6
    };
    img.onerror = function() {
      img.state = -1; // error
      callback &&
          callback(img, img.state, { w: 0, h: 0 });
    };
    img.onload = function() {
      if (img.complete ||
          img.readyState === "complete") { // IE8
        img.state = 1; // ok
        setTimeout(function() {
          callback &&
              callback(img, img.state, { w: img.width,
                                         h: img.height });
        }, 0);
      }
    };
    img.setAttribute("src", url);
  },

  // uuImage.getActualDimension
  // http://d.hatena.ne.jp/uupaa/20090602/1243933843
  getActualDimension: function(image) { // @param HTMLImageElement
                                        // @return Hash: { w, h }
    var run, mem, w, h, key = "actual";

    // for Firefox, Safari, Chrome
    if ("naturalWidth" in image) {
      return { w: image.naturalWidth, h: image.naturalHeight };
    }

    if ("src" in image) { // HTMLImageElement
      if (image[key] && image[key].src === image.src) {
        return image[key];
      }
      if (_ie) { // for IE
        run = image.runtimeStyle;
        mem = { w: run.width, h: run.height }; // keep runtimeStyle
        run.width  = "auto"; // override
        run.height = "auto";
        w = image.width;
        h = image.height;
        run.width  = mem.w; // restore
        run.height = mem.h;
      } else { // for Opera
        mem = { w: image.width, h: image.height }; // keep current style
        image.removeAttribute("width");
        image.removeAttribute("height");
        w = image.width;
        h = image.height;
        image.width  = mem.w; // restore
        image.height = mem.h;
      }
      return image[key] = { w: w, h: h, src: image.src }; // bond
    }
    // HTMLCanvasElement
    return { w: image.width, h: image.height };
  }
};

// --- initialize ---

// --- export ---
window.uuImage = _image;

})();


// === uuColor ===
// depend: uuMeta
/*
type RGBAHash = { r,g,b,a }
type RGBAValidHash = { r,g,b,a,valid }
type HexColorString = "#ffffff"
type RGBAColorString = "rgba(0,0,0,0)"
type W3CNamedColorString = "pink", "skyblue"

uuColor.parse(color, toRGBA = 0, valid = 0)
                         - return ["#ffffff", alpha, valid] or RGBAValidHash
uuColor.refHash - return Hash( { black: {}, gray: {}, ... } )
uuColor.hex(rgba) - return HexColorString("#ffffff")
uuColor.rgba(rgba) - return RGBAColorString("rgba(0,0,0,0)")
uuColor.arrange(rgba, h = 0, s = 0, v = 0) - return RGBAHash
uuColor.comple(rgba) - return RGBAHash
uuColor.gray(rgba) - return RGBAHash
uuColor.sepia(rgba) - return RGBAHash
uuColor.hex2rgba(hex) - return RGBAHash
uuColor.rgba2hsva(rgba) - return HSVAHash
uuColor.hsva2rgba(hsva) - return RGBAHash
uuColor.rgba2hsla(rgba) - return HSLAHash
uuColor.hsla2rgba(hsla) - return RGBAHash
 */
(function() {
var _color, // inner namespace
    _mm = uuMeta,
    _float = parseFloat,
    _math = Math,
    _round = _math.round,
    _hex = _mm.hex,
    _hash = { transparent: { hex: "#000000", num: 0,
                             r: 0, g: 0, b: 0, a: 0, valid: 1 } },
    PARSE_HEX = /^#(([\da-f])([\da-f])([\da-f])([\da-f]{3})?)$/,
    PARSE_PERCENT = /[\d\.]+%/g,
    PARSE_RGBA = /(rgba?)\((\d+),(\d+),(\d+)(?:,([\d\.]+))?\)/;

_color = {
  // uuColor.parse - parse color
  parse: function(
      color,    // @parem RGBAColorString
                //        /HexColorString
                //        /W3CNamedColorString:
      toRGBA) { // @param Boolean(= false): true = return RGBAValidHash
                //                          false = return Array
                // @return Array/RGBAValidHash:
                //          [ HexColorString, Number(alpha), valid ]
                //          { r, g, b, a, valid }
    var rv = _hash[color], m, n;

    !rv && (color = color.toLowerCase().replace(/\s+/g, ""),
            rv = _hash[color]);
    if (rv) {
      return toRGBA ? rv
                    : [rv.hex, rv.a, rv.valid];
    }
    if ( (m = PARSE_HEX.exec(color)) ) {
      rv = m[5] ? m[1]
                : [m[2], m[2], m[3], m[3], m[4], m[4]].join("");
      if (toRGBA) {
        n = parseInt(rv, 16);
        return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255,
                 a: 1, valid: 1 };
      }
      return ["#" + rv, 1, 1];
    }
    m = PARSE_RGBA.exec(color);
    if (!m) {
      m = PARSE_RGBA.exec(color.replace(PARSE_PERCENT, function(n) {
        return _math.min((_float(n) || 0) * 2.55, 255) | 0
      }));
    }
    if (m) {
      return toRGBA ? { r: m[2] | 0, g: m[3] | 0, b: m[4] | 0,
                        a: m[1] === "rgb" ? 1 : _float(m[5]), valid: 1 }
                    : [["#", _hex[m[2]], _hex[m[3]], _hex[m[4]]].join(""),
                       m[1] === "rgb" ? 1 : _float(m[5]), 1];
    }
    return toRGBA ? { r: 0, g: 0, b: 0, a: 0, valid: 0 }
                  : ["#000000", 0, 0];
  },

  // uuColor.refHash - return color hash table
  refHash: function() {
    return _hash;
  },

  // uuColor.hex - return Hex Color String( "#ffffff" )
  hex: function(rgba) { // @param RGBAHash: Hash( { r,g,b,a })
                        // @return HexColorString( "#ffffff" )
    return ["#", _hex[rgba.r], _hex[rgba.g], _hex[rgba.b]].join("");
  },

  // uuColor.rgba - return RGBA Color String( "rgba(0,0,0,0)" )
  rgba: function(rgba) { // @param RGBAHash: Hash( { r,g,b,a })
                         // @return RGBAColorString( "rgba(0,0,0,0)" )
    return "rgba(" + [rgba.r, rgba.g, rgba.b, rgba.a].join(",") + ")";
  },

  // uuColor.arrange - arrangemented color(Hue, Saturation and Value)
  //    Hue is absolure value,
  //    Saturation and Value is relative value.
  arrange: function(rgba, // @param RGBAHash: Hash( { r,g,b,a })
                    h,    // @param Number(=0): Hue (from -360 to 360)
                    s,    // @param Number(=0): Saturation (from -100 to 100)
                    v) {  // @param Number(=0): Value (from -100 to 100)
                          // @return RGBAHash:
    var rv = _color.rgba2hsva(rgba), r = 360;
    rv.h += h, rv.h = (rv.h > r) ? rv.h - r : (rv.h < 0) ? rv.h + r : rv.h;
    rv.s += s, rv.s = (rv.s > 100) ? 100 : (rv.s < 0) ? 0 : rv.s;
    rv.v += v, rv.v = (rv.v > 100) ? 100 : (rv.v < 0) ? 0 : rv.v;
    return _color.hsva2rgba(rv);
  },

  // uuColor.comple - complementary color
  comple: function(rgba) { // @param RGBAHash: Hash( { r,g,b,a })
                           // @return RGBAHash:
    return { r: rgba.r ^ 255, g: rgba.g ^ 255, b: rgba.b ^ 255, a: rgba.a };
  },

  // uuColor.gray - gray color (G channel method)
  gray: function(rgba) { // @param RGBAHash: Hash( { r,g,b,a })
                         // @return RGBAHash:
    return { r: rgba.g, g: rgba.g, b: rgba.g, a: rgba.a };
  },

  // uuColor.sepia - sepia color
  sepia: function(rgba) { // @param RGBAHash: Hash( { r,g,b,a })
                          // @return RGBAHash:
    var r = rgba.r, g = rgba.g, b = rgba.b,
        y = 0.2990 * r + 0.5870 * g + 0.1140 * b, u = -0.091, v = 0.056;

    r = y + 1.4026 * v;
    g = y - 0.3444 * u - 0.7114 * v;
    b = y + 1.7330 * u;
    r *= 1.2;
    b *= 0.8;
    return { r: r < 0 ? 0 : r > 255 ? 255 : r | 0,
             g: g < 0 ? 0 : g > 255 ? 255 : g | 0,
             b: b < 0 ? 0 : b > 255 ? 255 : b | 0, a: rgba.a };
  },

  // uuColor.hex2rgba - convert "#ffffff" to RGBAHash
  hex2rgba: function(hex) { // @param HexColorString: String( "#ffffff" )
                            // @return RGBAHash: Hash( { r,g,b,a } )
    var n = parseInt(hex.slice(1), 16);
    return { r: (n >> 16) & 0xff, g: (n >> 8) & 0xff, b: n & 0xff, a: 1 };
  },

  // uuColor.rgba2hsva
  rgba2hsva: function(rgba) { // @param RGBAHash:
                              // @return HSVAHash:
    var r = rgba.r / 255, g = rgba.g / 255, b = rgba.b / 255,
        max = _math.max(r, g, b), diff = max - _math.min(r, g, b),
        h = 0, s = max ? _round(diff / max * 100) : 0, v = _round(max * 100);
    if (!s) {
      return { h: 0, s: 0, v: v, a: rgba.a };
    }
    h = (r === max) ? ((g - b) * 60 / diff) :
        (g === max) ? ((b - r) * 60 / diff + 120)
                    : ((r - g) * 60 / diff + 240);
    // HSVAHash( { h:360, s:100, v:100, a:1.0 } )
    return { h: (h < 0) ? h + 360 : h, s: s, v: v, a: rgba.a };
  },

  // uuColor.hsva2rgba
  hsva2rgba: function(hsva) { // @param HSVAHash:
                              // @return RGBAHash:
    var h = (hsva.h >= 360) ? 0 : hsva.h,
        s = hsva.s / 100,
        v = hsva.v / 100,
        a = hsva.a,
        h60 = h / 60, matrix = h60 | 0, f = h60 - matrix,
        v255, p, q, t, w;
    if (!s) {
      h = _round(v * 255);
      return { r: h, g: h, b: h, a: a };
    }
    v255 = v * 255,
    p = _round((1 - s) * v255),
    q = _round((1 - (s * f)) * v255),
    t = _round((1 - (s * (1 - f))) * v255),
    w = _round(v255);
    switch (matrix) {
      case 0: return { r: w, g: t, b: p, a: a };
      case 1: return { r: q, g: w, b: p, a: a };
      case 2: return { r: p, g: w, b: t, a: a };
      case 3: return { r: p, g: q, b: w, a: a };
      case 4: return { r: t, g: p, b: w, a: a };
      case 5: return { r: w, g: p, b: q, a: a };
    }
    return { r: 0, g: 0, b: 0, a: a };
  },

  // uuColor.rgba2hsla
  rgba2hsla: function(rgba) { // @param RGBAHash:
                              // @return HSLAHash:
    var r = rgba.r / 255,
        g = rgba.g / 255,
        b = rgba.b / 255,
        max = Math.max(r, g, b),
        min = Math.min(r, g, b),
        diff = max - min,
        h = 0, s = 0, l = (min + max) / 2;

    if (l > 0 && l < 1) {
      s = diff / (l < 0.5 ? l * 2 : 2 - (l * 2));
    }
    if (diff > 0) {
      if (max === r && max !== g) {
        h += (g - b) / diff;
      } else if (max === g && max !== b) {
        h += (b - r) / diff + 2;
      } else if (max === b && max !== r) {
        h += (r - g) / diff + 4;
      }
      h *= 60;
    }
    return { h: h, s: _round(s * 100), l: _round(l * 100), a: rgba.a };
  },

  // uuColor.hsla2rgba - ( h: 0-360, s: 0-100, l: 0-100, a: alpha )
  hsla2rgba: function(hsla) { // @param HSLAHash:
                              // @return RGBAHash:
    var h = (hsla.h === 360) ? 0 : hsla.h,
        s = hsla.s / 100,
        l = hsla.l / 100,
        r, g, b, s1, s2, l1, l2;

    if (h < 120) {
      r = (120 - h) / 60, g = h / 60, b = 0;
    } else if (h < 240) {
      r = 0, g = (240 - h) / 60, b = (h - 120) / 60;
    } else {
      r = (h - 240) / 60, g = 0, b = (360 - h) / 60;
    }
    s1 = 1 - s;
    s2 = s * 2;

    r = s2 * (r > 1 ? 1 : r) + s1;
    g = s2 * (g > 1 ? 1 : g) + s1;
    b = s2 * (b > 1 ? 1 : b) + s1;

    if (l < 0.5) {
      r *= l, g *= l, b *= l;
    } else {
      l1 = 1 - l;
      l2 = l * 2 - 1;
      r = l1 * r + l2;
      g = l1 * g + l2;
      b = l1 * b + l2;
    }
    return { r: _round(r * 255), g: _round(g * 255),
             b: _round(b * 255), a: hsla.a };
  }
};

// --- initialize ---
function init() {
  var key, val, num, v, i = 0, item = (
// FAMICOM(R) Named Color(from "FC00" to "FC3F")
"7b7b7bfc00,0000fffc01,0000bdfc02,4229bdfc03,940084fc04,ad0021fc05,8c1000fc06,"+
"8c1000fc07,522900fc08,007300fc09,006b00fc0a,005a00fc0b,004252fc0c,000000fc0d,"+
"000000fc0e,000000fc0f,bdbdbdfc10,0073f7fc11,0052f7fc12,6b42fffc13,de00cefc14,"+
"e7005afc15,f73100fc16,e75a10fc17,ad7b00fc18,00ad00fc19,00ad00fc1a,00ad42fc1b,"+
"008c8cfc1c,000000fc1d,000000fc1e,000000fc1f,f7f7f7fc20,39bdfffc20,6b84fffc22,"+
"9473f7fc23,f773f7fc24,f75294fc25,f77352fc26,ffa542fc27,f7b500fc28,b5f710fc29,"+
"5ade52fc2a,52f794fc2b,00efdefc2c,737373fc2d,000000fc2e,000000fc2f,fffffffc30,"+
"a5e7fffc31,b5b5f7fc32,d6b5f7fc33,f7b5f7fc34,ffa5c6fc35,efceadfc36,ffe7adfc37,"+
"ffde7bfc38,d6f773fc39,b5f7b5fc3a,b5f7d6fc3b,00fffffc3c,f7d6f7fc3d,000000fc3e,"+
"000000fc3f,"+
// W3C Named Color
"000000black,888888gray,ccccccsilver,ffffffwhite,ff0000red,"+
"ffff00yellow,00ff00lime,00ffffaqua,00ffffcyan,0000ffblue,ff00fffuchsia,"+
"ff00ffmagenta,880000maroon,888800olive,008800green,008888teal,000088navy,"+
"880088purple,696969dimgray,808080gray,a9a9a9darkgray,c0c0c0silver,"+
"d3d3d3lightgrey,dcdcdcgainsboro,f5f5f5whitesmoke,fffafasnow,708090slategray,"+
"778899lightslategray,b0c4delightsteelblue,4682b4steelblue,5f9ea0cadetblue,"+
"4b0082indigo,483d8bdarkslateblue,6a5acdslateblue,7b68eemediumslateblue,"+
"9370dbmediumpurple,f8f8ffghostwhite,00008bdarkblue,0000cdmediumblue,"+
"4169e1royalblue,1e90ffdodgerblue,6495edcornflowerblue,87cefalightskyblue,"+
"add8e6lightblue,f0f8ffaliceblue,191970midnightblue,00bfffdeepskyblue,"+
"87ceebskyblue,b0e0e6powderblue,2f4f4fdarkslategray,00ced1darkturquoise,"+
"afeeeepaleturquoise,f0ffffazure,008b8bdarkcyan,20b2aalightseagreen,"+
"48d1ccmediumturquoise,40e0d0turquoise,7fffd4aquamarine,e0fffflightcyan,"+
"00fa9amediumspringgreen,7cfc00lawngreen,00ff7fspringgreen,7fff00chartreuse,"+
"adff2fgreenyellow,2e8b57seagreen,3cb371mediumseagreen,66cdaamediumaquamarine,"+
"98fb98palegreen,f5fffamintcream,006400darkgreen,228b22forestgreen,"+
"32cd32limegreen,90ee90lightgreen,f0fff0honeydew,556b2fdarkolivegreen,"+
"6b8e23olivedrab,9acd32yellowgreen,8fbc8fdarkseagreen,9400d3darkviolet,"+
"8a2be2blueviolet,dda0ddplum,d8bfd8thistle,8b008bdarkmagenta,9932ccdarkorchid,"+
"ba55d3mediumorchid,da70d6orchid,ee82eeviolet,e6e6falavender,"+
"c71585mediumvioletred,bc8f8frosybrown,ff69b4hotpink,ffc0cbpink,"+
"ffe4e1mistyrose,ff1493deeppink,db7093palevioletred,e9967adarksalmon,"+
"ffb6c1lightpink,fff0f5lavenderblush,cd5c5cindianred,f08080lightcoral,"+
"f4a460sandybrown,fff5eeseashell,dc143ccrimson,ff6347tomato,ff7f50coral,"+
"fa8072salmon,ffa07alightsalmon,ffdab9peachpuff,ffffe0lightyellow,"+
"b22222firebrick,ff4500orangered,ff8c00darkorange,ffa500orange,"+
"ffd700gold,fafad2lightgoldenrodyellow,8b0000darkred,a52a2abrown,"+
"a0522dsienna,b8860bdarkgoldenrod,daa520goldenrod,deb887burlywood,f0e68ckhaki,"+
"fffacdlemonchiffon,d2691echocolate,cd853fperu,bdb76bdarkkhaki,bdb76btan,"+
"eee8aapalegoldenrod,f5f5dcbeige,ffdeadnavajowhite,ffe4b5moccasin,"+
"ffe4c4bisque,ffebcdblanchedalmond,ffefd5papayawhip,fff8dccornsilk,"+
"f5deb3wheat,faebd7antiquewhite,faf0e6linen,fdf5e6oldlace,fffaf0floralwhite,"+
"fffff0ivory").split(",");

  while ( (v = item[i++]) ) {
    key = v.slice(6);
    val = v.slice(0, 6);
    num = parseInt(val, 16);
    _hash[key] = { hex: "#" + val,
                   num: num,
                   r: (num >> 16) & 255,
                   g: (num >> 8) & 255,
                   b: num & 255,
                   a: 1,
                   valid: 1 };
  }
}
init();

// --- export ---
window.uuColor = _color; // window.uuColor

})(); // uuColor scope


// === uuCodec ===
// depend: none
/*
uuCodecDataURI.decode(str) - return { mime, data }
uuCodecHex.decode("%00%01") - return [0x00, 0x01]
uuCodecBase64.encode(ByteArray, safe64) - return Base64String/URLSafe64String
uuCodecBase64.decode(str) - return ByteArray
 */
(function() {
var _datauri,   // inner namespace
    _hex,       // inner namespace
    _base64,    // inner namespace
    _b64code =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
    _b64hash = { "=": 0 };

_datauri = {
  // uuCodecDataURI.decode
  //    ...
  //    data:text/css,.picture%20%7B%20background%3A%20none%3B%20%7D
  decode: function(str) { // @param String:
                          // @return Hash: { mime, data }
    var m, mime, data;

    if ( (m = /^data:([\w\/]+)(;base64)?,/.exec(str)) ) {
      mime = m[1];
      data = str.slice(m[0].length);
      data = m[2] ? _base64.decode(decodeURIComponent(data)) // base64
                  : _hex.decode(data); // decodeURI weak("%00")
    }
    return { mime: mime || "", data: data || [] };
  }
};

_hex = {
  // uuCodecHex.decode - "%00%01" to [0x00, 0x01]
  decode: function(hex) { // @param HexString: "%00" + ASCII string
                          // @return ByteArray:
    var rv = [], ri = -1, c = 0, i = 0, iz = hex.length,
        mark = "%".charCodeAt(0);

    if (!hex.length) {
      return [];
    }
    if (hex.length >= 3) {
      if (hex.charAt(hex.length - 1) === "%" || // tail "...%"
          hex.charAt(hex.length - 2) === "%") { // tail "..%x"
        return []; // bad data
      }
    }
    while (i < iz) {
      c = hex.charCodeAt(i++);
      if (c === mark) {
        rv[++ri] = parseInt(hex.charAt(i) + hex.charAt(i + 1), 16);
        i += 2;
      } else {
        rv[++ri] = c;
      }
    }
    return rv;
  }
};

_base64 = {
  // uuCodecBase64.encode - ByteArray to Base64String
  encode: function(ary,         // @param ByteArray: array( [0x0, ... ] )
                   URLSafe64) { // @param Boolean(= true): true = URLSafe
                                // @return Base64String/URLSafe64String:
    var rv = [], pad = 0, code = _b64code, c = 0, i = 0, iz;

    switch (ary.length % 3) {
    case 1: ary.push(0); ++pad;
    case 2: ary.push(0); ++pad;
    }
    iz = ary.length;

    while (i < iz) {
      c = (ary[i++] << 16) | (ary[i++] << 8) | (ary[i++]);
      rv.push(code.charAt((c >>> 18) & 0x3f),
              code.charAt((c >>> 12) & 0x3f),
              code.charAt((c >>>  6) & 0x3f),
              code.charAt( c         & 0x3f));
    }
    switch (pad) {
    case 2: rv[rv.length - 2] = "=";
    case 1: rv[rv.length - 1] = "=";
    }

    if (URLSafe64 === void 0 || URLSafe64) {
      return rv.join("").replace(/\+/g, "-").
                         replace(/\//g, "_").replace(/=/g, "");
    }
    return rv.join("");
  },

  // uuCodecBase64.decode - Base64String to ByteArray
  decode: function(b64) { // @param Base64String/URLSafe64String:
                          // @return ByteArray:
    if (typeof b64 !== "string" || !b64.length) {
      return []; // empty
    }

    // URLBase64Charcter("-", "_") convert to ("+", "/")
    b64 = b64.replace(/-/g, "+").replace(/_/g, "/").replace(/=/g, "");

    if (/[^A-Za-z0-9\+\/]/.test(b64)) {
      return []; // bad data
    }

    var rv = [], pad = 0, hash = _b64hash, c = 0, i = 0, iz;

    switch (b64.length % 4) { // pad length( "=" or "==" or "" )
    case 2: b64 += "="; ++pad;
    case 3: b64 += "="; ++pad;
    }

    iz = b64.length;
    while (i < iz) {                    // 00000000|00000000|00000000
      c = (hash[b64.charAt(i++)] << 18) // 111111  |        |
        | (hash[b64.charAt(i++)] << 12) //       11|1111    |
        | (hash[b64.charAt(i++)] <<  6) //         |    1111|11
        |  hash[b64.charAt(i++)]        //         |        |  111111
      rv.push((c >>> 16) & 0xff, (c >>> 8) & 0xff, c & 0xff);
    }
    rv.length -= [0,1,2][pad]; // cut tail
    return rv;
  }
};

// --- initialize ---
(function() {
  // make hash
  for (var i = 0, iz = _b64code.length; i < iz; ++i) {
    _b64hash[_b64code.charAt(i)] = i;
  }
})();

// --- export ---
window.uuCodecDataURI = _datauri;   // window.uuCodecDataURI
window.uuCodecHex     = _hex;       // window.uuCodecHex
window.uuCodecBase64  = _base64;    // window.uuCodecBase64

})(); // uuCodec scope

// === uuStyle ===
// depend: uuMeta, uuColor
/*
uuStyle.toPixel(node, value) - return pixel value
uuStyle.getPixel(node, prop) - return pixel value
uuStyle.getBGImg(node) - return "http://..." or ""
uuStyle.getBGColor(node, ancestor = false, toRGBA = false)
                                        - return [color, alpha] or { r,g,b,a }
uuStyle.getViewport() - return { w, h, sx, sy }
uuStyle.getRect(node) - return { x, y, w, h, ow, oh }
uuStyle.setRect(node, { x, y, w, h })
uuStyle.getRelPos(node) - return { x, y }
 */
(function() {
var _style, // inner namespace
    _mm = uuMeta,
    _win = window,
    _doc = document,
    _int = parseInt,
    _float = parseFloat,
    _ie = _mm.ie,
    _opera = _mm.opera,
    _webkit = _mm.webkit,
    _ieroot = _mm.quirks ? "body" : "documentElement",
    _runstyle = _mm.runstyle,
    TRANSPARENT = "transparent",
    TRANS_RGBA = "rgba(0, 0, 0, 0)", // webkit format
    IMPORTANT = "important",
    POSITION = "position",
    ABSOLUTE = "absolute",
    DISPLAY = "display",
    BLOCK = "block",
    LEFT = "left";

_style = {
  // uuStyle.toPixel - covert unit
  //    toPixel(node, 123)    -> 123
  //    toPixel(node, "12px") -> 12
  //    toPixel(node, "12pt") -> 16
  //    toPixel(node, "12em") -> 192
  toPixel: function(node,         // @param Node: context
                    value,        // @param String/Number:
                    accurately) { // @param Boolena(= false): false = quick
                                  // @return Number: pixel value
    var rv, st, rs, mem1, mem2, mem3, fontSize;

    if (typeof value === "string") {
      if (value.lastIndexOf("px") > 0) { // value is pixel unit
        return _int(value) || 0;
      }

      if (!accurately) {
        // quick
        rv = _float(value);
        if (value.lastIndexOf("pt") > 0) {
          rv *= 4 / 3; // 1.333...
        } else if (value.lastIndexOf("em") > 0) {
          fontSize = (_ie ? node[_runstyle]
                          : _runstyle(node, "")).fontSize;
          if (fontSize.lastIndexOf("pt") > 0) { // 12pt * 1.333 = 16px
            rv *= _float(fontSize) * 4 / 3;
          } else {
            rv *= _float(fontSize); // 12px
          }
        }
        return _int(rv) || 0;
      }

      st = node.style, mem1 = st[LEFT];
      if (_ie) {
        rs = node.runtimeStyle, mem2 = rs[LEFT]; // keep !important value
        // overwrite
        rs[LEFT] = node[_runstyle][LEFT];
        st[LEFT] = value;
        // get pixel
        value = st.pixelLeft;
        // restore
        st[LEFT] = mem1;
        rs[LEFT] = mem2;
      } else {
        // overwrite
        if (_webkit) {
          mem2 = st.getPropertyValue(POSITION);
          mem3 = st.getPropertyValue(DISPLAY);
          st.setProperty(POSITION, ABSOLUTE, IMPORTANT);
          st.setProperty(DISPLAY,  BLOCK,    IMPORTANT);
        }
        st.setProperty(LEFT, value, IMPORTANT);
        // get pixel
        value = _int(_runstyle(node, "")[LEFT]);
        // restore
        st.removeProperty(LEFT);
        st.setProperty(LEFT, mem1, "");
        if (_webkit) {
          st.removeProperty(POSITION);
          st.removeProperty(DISPLAY);
          st.setProperty(POSITION, mem2, "");
          st.setProperty(DISPLAY,  mem3, "");
        }
      }
    }
    return value || 0;
  },

  // uuStyle.getPixel - get pixel value
  //    getPixel(node, "left")
  //    getPixel(node, "width")
  getPixel: function(node,   // @param Node:
                     prop) { // @param String: style property name
                             // @return Number: pixel value
    function dim(horizontal) {
      var r = node.getBoundingClientRect();
      return horizontal ? (r.right - r.left) : (r.bottom - r.top);
    }
    var rv;

    if (_ie) {
      switch (prop) {
      case "width":  return node.clientWidth  || dim(1);
      case "height": return node.clientHeight || dim(0);
      }
      rv = node[_runstyle][prop];
      (rv === "auto") && (rv = _style.toPixel(node, rv));
    } else {
      rv = _runstyle(node, "")[prop];
    }
    return _int(rv) || 0;
  },

  // uuStyle.getBGImg - get background-image url
  getBGImg: function(node) { // @param Node:
                             // @return String: "http://..." or ""
    var bg = "backgroundImage", m,
        url = _ie ? (node.style[bg] || node[_runstyle][bg])
                  : _runstyle(node, "")[bg];
    if (url.indexOf(",") < 0) { // skip CSS3 multiple background-image
      if (url) {
        if ( (m = /^url\((.*)\)$/.exec(url)) ) {
          return m[1].replace(/^\s*[\"\']?|[\"\']?\s*$/g, ""); // trim quote
        }
      }
    }
    return "";
  },

  // uuStyle.getBGColor - get background-color (from ancestor)
  // depend: uuColor
  getBGColor:
      function(node,     // @param Node:
               ancestor, // @param Boolean(= false):
               toRGBA) { // @param Boolean(= false): true = return RGBAHash
                         //                          false = return Array
                         // @return Array: [HexColorString, Alpha]
    function isZero(color) {
      return color === TRANSPARENT || color === TRANS_RGBA;
    }
    var bgc = "backgroundColor", n = node, color = TRANSPARENT;

    if (!ancestor) {
      return _ie ? (n.style[bgc] || n[_runstyle][bgc])
                 : _runstyle(n, "")[bgc];
    }
    while (n && n !== _doc && isZero(color)) {
      if ((_ie && n[_runstyle]) || !_ie) {
        color = _ie ? n[_runstyle][bgc]
                    : _runstyle(n, "")[bgc];
      }
      n = n.parentNode;
    }
    if (toRGBA) {
      return isZero(color) ? { r: 255, g: 255, b: 255, a: 1 }
                           : uuColor.parse(color, 1);
    }
    return isZero(color) ? ["white", 1]
                         : uuColor.parse(color);
  },

  // uuStyle.getViewport - get viewport dimension and scroll offset
  getViewport: function() { // @return { w, h, sx, sy }
    var e = _win;

    if (_ie) {
      e = _doc[_ieroot];
      return { w: e.clientWidth  || e.scrollWidth,
               h: e.clientHeight || e.scrollHeight,
               sx: e.scrollLeft,
               sy: e.scrollTop };
    }
    // "window.pageXOffset" alias "window.scrollX" in gecko, webkit
    return { w: e.innerWidth,
             h: e.innerHeight,
             sx: e.pageXOffset,
             sy: e.pageYOffset };
  },

  // uuStyle.getRect - get element absolute position and rectangle
  getRect: function(node) { // @param Node:
                            // @return Hash: { x, y, w, h, ow, oh }
    var e, x = 0, y = 0, w = 0, h = 0, fix = 0, rect, vp;

    if (node.getBoundingClientRect) {
      // get relative position
      rect = node.getBoundingClientRect();
      fix = (_ie && node.parentNode === _doc.body) ? 2 : 0;
      // to absolute position
      vp = _style.getViewport();
      x = rect.left + vp.sx - fix;
      y = rect.top  + vp.sy - fix;
      w = rect.right - rect.left;
      h = rect.bottom - rect.top;
    } else {
      // get absolute position(for Fx2)
      e = node;
      while (e) {
        x += e.offsetLeft || 0;
        y += e.offsetTop  || 0;
        e  = e.offsetParent;
      }
    }
    return {
      // element position(absolute)
      x: x,
      y: y,
      // element dimension(style.width + padding)
      w: node.clientWidth  || w,
      h: node.clientHeight || h,
      // element dimension(style.width + padding + border)
      ow: node.offsetWidth,
      oh: node.offsetHeight
    };
  },

  // uuStyle.setRect - set element rect
  setRect: function(node,   // @param Node:
                    rect) { // @param Hash: { x, y, w, h }
    var s = node.style;

    if (_ie || _opera) {
      if ("x" in rect) { s.pixelLeft   = rect.x; }
      if ("y" in rect) { s.pixelTop    = rect.y; }
      if ("w" in rect) { s.pixelWidth  = rect.w > 0 ? rect.w : 0; }
      if ("h" in rect) { s.pixelHeight = rect.h > 0 ? rect.h : 0; }
    } else {
      if ("x" in rect) { s.left   = rect.x + "px"; }
      if ("y" in rect) { s.top    = rect.y + "px"; }
      if ("w" in rect) { s.width  = (rect.w > 0 ? rect.w : 0) + "px"; }
      if ("h" in rect) { s.height = (rect.h > 0 ? rect.h : 0) + "px"; }
    }
  },

  // uuStyle.getRelPos - get element relative position
  getRelPos: function(node) { // @param Node:
                              // @return Hash: { x, y }
    var x = 0, y = 0, ns,
        hash = { relative: 1, absolute: 1 };

    while (node) {
      x   += node.offsetLeft || 0;
      y   += node.offsetTop  || 0;
      node = node.offsetParent;
      ns   = _ie ? node[_runstyle]
                 : _runstyle(node, "");
      if (hash[ns.position]) {
        break;
      }
    }
    return { x: x, y: y };
  }
};

// --- initialize ---

// --- export ---
_win.uuStyle = _style; // window.uuStyle

})(); // uuStyle scope


// === uuStyle.opacity ===
// depend: uuMeta
/*
uuStyle.getOpacity(elm) - return 0.0 ~ 1.0
uuStyle.setOpacity(elm, opacity = 1.0, diff = 0)
 */
(function() {
var _mm = uuMeta,
    _float = parseFloat,
    _runstyle = _mm.runstyle,
    ALPHA = "DXImageTransform.Microsoft.Alpha";

// uuStyle.setOpacity - set opacity value(from 0.0 to 1.0)
function setOpacity(elm,     // @param Node:
                    opacity, // @param Number: float(from 0.0 to 1.0)
                    diff) {  // @param Boolean(= false):
  if (diff) {
    opacity += _float(elm.style.opacity || _runstyle(elm, "").opacity);
  }
  elm.style.opacity = (opacity > 0.999) ? 1
                    : (opacity < 0.001) ? 0 : opacity;
}

function setOpacityIE(elm, opacity, diff) {
  var es = elm.style, obj;

  if (es.uuopacity === void 0) {
    es.uuopacity = 1;
    (es.filter.indexOf(ALPHA) < 0) && (es.filter += " progid:" + ALPHA);
    if (!_mm.iemode8 && elm.currentStyle.width === "auto") {
      es.zoom = 1; // IE6, IE7: force "hasLayout"
    }
  }
  if (diff) {
    opacity += _float(es.uuopacity);
  }
  es.uuopacity = opacity = (opacity > 0.999) ? 1
                         : (opacity < 0.001) ? 0 : opacity;
  obj = elm.filters.item(ALPHA);
  obj.Opacity = (opacity * 100) | 0;
  obj.Enabled = (opacity === 1 || opacity === 0) ? false : true;
  es.visibility = (opacity === 0) ? "hidden" : "";
}

// uuStyle.getOpacity - get opacity value(from 0.0 to 1.0)
function getOpacity(elm) { // @param Node:
                           // @return Number: float(from 0.0 to 1.0)
  return _float(elm.style.opacity || _runstyle(elm, "").opacity);
}

function getOpacityIE(elm) {
  var rv = elm.style.uuopacity;

  return (rv === void 0) ? 1 : rv;
}

// --- initialize ---

// --- export ---
window.uuStyle.getOpacity = _mm.ie ? getOpacityIE : getOpacity;
window.uuStyle.setOpacity = _mm.ie ? setOpacityIE : setOpacity;

})(); // uuStyle.opacity scope


// === uuStyle.shadow ===
// depend: uuMeta, uuColor
/*
uuStyle.getTextShadow(elm) - return { color, ox, oy, blur }
uuStyle.setTextShadow(elm, color, ox, oy, blur)
 */
(function() {
var _mm = uuMeta,
    TRANSPARENT = "transparent",
    DXIMG = "DXImageTransform.Microsoft.",
    SHADOW = DXIMG + "Shadow",
    BLUR = DXIMG + "MotionBlur";

// uuStyle.setTextShadow - set text-shadow value
function setTextShadow(elm,    // @param Node:
                       color,  // @param Array: ColorString/RGBAHash color
                       ox,     // @param Array: shadow offsetX (with unit)
                       oy,     // @param Array: shadow offsetY (with unit)
                       blur) { // @param Array: shadow blur (with unit)
  var rv = [], c, i = 0, iz = color.length;

  for (; i < iz; ++i) {
    c = color[i];
    rv.push([(typeof c === "string") ? c : uuColor.hex(c), " ",
             ox[i], " ", oy[i], " ", blur[i]].join(""));
  }
  elm.style.textShadow = rv.join(",");
}

function setTextShadowIE(elm, color, ox, oy, blur) {
  if (!color.length) {
    color[0] = TRANSPARENT;
    ox[0] = 0;
    oy[0] = 0;
    blur[0] = 0;
  }

  var es = elm.style, obj,
      x = uuStyle.toPixel(elm, ox[0]),
      y = uuStyle.toPixel(elm, oy[0]),
      rgba = (typeof color[0] === "string") ? uuColor.parse(color[0], 1)
                                            : color[0],
      dir = Math.atan2(y, x) * (180 / Math.PI) + 90,
      shadowStrength,
      blurStrength;

  if (x || y) {
    shadowStrength = Math.max(Math.abs(x), Math.abs(y));
    blurStrength = Math.min(uuStyle.toPixel(elm, blur[0]) / 2.5, 10);
  } else {
    shadowStrength = 0;
    blurStrength = 5;
  }

  if (es.uutextshadow === void 0) {
    (es.filter.indexOf(SHADOW) < 0) && (es.filter += " progid:" + SHADOW);
    (es.filter.indexOf(BLUR)   < 0) && (es.filter += " progid:" + BLUR);
    if (elm.currentStyle.display === "inline") {
      elm.style.display = "inline-block";
    }
    if (!_mm.iemode8 && elm.currentStyle.width === "auto") {
      es.zoom = 1; // IE6, IE7: force "hasLayout"
    }
  }

  es.uutextshadow = { color: color, ox: ox, oy: oy, blur: blur };

  obj = elm.filters.item(SHADOW);
  obj.Color = uuColor.hex(rgba);
  obj.Strength = shadowStrength;
  obj.Direction = dir;
  obj.Enabled = !rgba.a ? false : true;

  obj = elm.filters.item(BLUR);
  obj.Add = true;
  obj.Strength = blurStrength;
  obj.Direction = dir;
  obj.Enabled = !rgba.a ? false : true;
}

// uuStyle.getTextShadow - get text-shadow value
function getTextShadow(elm) { // @param Node:
                              // @return Hash: { color, ox, oy, blur }
  return uuCSSValidator["text-shadow"](_mm.runstyle(elm, "").textShadow);
}

function getTextShadowIE(elm) {
  var rv = elm.style.uutextshadow

  return (rv === void 0) ? { color: [TRANSPARENT], ox: [0], oy: [0], blur: [0] }
                         : rv;
}

// --- initialize ---

// --- export ---
window.uuStyle.getTextShadow = _mm.ie ? getTextShadowIE : getTextShadow;
window.uuStyle.setTextShadow = _mm.ie ? setTextShadowIE : setTextShadow;

})(); // uuStyle.shadow scope


// === uuStyleSheet ===
// depend: uuMeta
/*
uuStyleSheet.create(id) - return object
uuStyleSheet.insertRule(id, expr, decl, index = last) - return index
uuStyleSheet.removeRule(id, index = last)
uuStyleSheet.removeAllRules(id)
 */
(function() {
var _ss, // inner namespace
    _mm = uuMeta,
    _win = window,
    _doc = document,
    _ie = _mm.ie,
    _sheet = {}, // private style sheet
    TRIM = /^\s+|\s+$/g;

_ss = {
  // uuStyleSheet.create - create StyleSheet
  create: function(id) { // @param String: StyleSheet id
                         // @return StyleSheet: new or already object
    if (id in _sheet) { // already exists
      return _sheet[id];
    }
    if (_ie) {
      _sheet[id] = _doc.createStyleSheet();
    } else {
      var elm = _doc.createElement("style");
      elm.appendChild(_doc.createTextNode(""));
      _sheet[id] = _doc.getElementsByTagName("head")[0].appendChild(elm);
    }
    return _sheet[id];
  },

  // uuStyleSheet.insertRule - insert CSS rule
  insertRule: function(id,      // @param String: StyleSheet id
                       expr,    // @param String: css selector
                       decl,    // @param String: css declaration
                       index) { // @param Number(= last): insertion position
                                // @return Number: inserted rule index
                                //                 or -1(error)
    if (!(id in _sheet)) { return -1; }

    var r = _sheet[id];
    if (_ie) {
      index = index === void 0 ? r.rules.length : index;
      r.addRule(expr.replace(TRIM, ""), decl.replace(TRIM, ""), index);
    } else {
      index = index === void 0 ? r.sheet.cssRules.length : index;
      index = r.sheet.insertRule(expr + "{" + decl + "}", index);
      if (_mm.opera && _mm.uaver < 9.5) { // Opera90 bug
        index = r.sheet.cssRules.length - 1;
      }
    }
    return index;
  },

  // uu.style.removeRule - remove CSS rule
  removeRule: function(id,      // @param String: StyleSheet id
                       index) { // @param Number(= last): deletion position
    if (!(id in _sheet)) { return; }

    var r = _sheet[id];
    if (_ie) {
      index = (index === void 0) ? r.rules.length - 1 : index;
      (index >= 0) && r.removeRule(index);
    } else {
      index = (index === void 0) ? r.sheet.cssRules.length - 1 : index;
      (index >= 0) && r.sheet.deleteRule(index);
    }
  },

  // uuStyleSheet.removeAllRules - remove all CSS rules
  removeAllRules: function(id) {
    if (!(id in _sheet)) { return; }

    var r = _sheet[id],
        i = _ie ? r.rules.length
                : r.sheet.cssRules.length;
    while (i--) {
      _ie ? r.removeRule(i)
          : r.sheet.deleteRule(i);
    }
  }
};

// --- initialize ---

// --- export ---
_win.uuStyleSheet = _ss;

})(); // uuStyleSheet scope

// === uuQuery ===
// depend: uuMeta, uuStyleSheet, [uuStyle], [uuColor]
/*
uuQuery(expr, context = document) - return NodeArray
uuQuery.id(expr) - return Node
uuQuery.tag(expr, context = document) - return NodeArray
uuQuery.className(expr, context = document) - return NodeArray
 */
(function() {
var _query, // inner namespace
    _mm = uuMeta,
    _ss = uuStyleSheet,
    _win = window,
    _doc = document,
    _visited = !!_win.UUQUERY_ENABLE_VISITED, // 1 = :visited activate
    _int = parseInt,
    _float = parseFloat,
    _ie = _mm.ie,
    _gecko = _mm.gecko,
    _iemode8 = _mm.iemode8,
    _innerText = _gecko ? "textContent" : "innerText",
    _runstyle = _mm.runstyle,
    _ssid = "uuQuery", // StyleSheet ID
    _toArray = _mm.toArray,
    // root - ref document root element (<html>)
    _rootElement = _doc.documentElement ||
                   _doc.getElementsByTagName("html")[0],
    _headElement = _doc.getElementsByTagName("head")[0],
    // --- content-type cache (1: HTML, 2: XML) ---
    _contentTypeCache = { /* quid: contentType */ },
    // tag dict( { a: "A", A: "A", ... } )
    _htmlTag = {},
    _xmlTag = {},
    _uid2node, // lazy
    UNIQUEID = "uuqid",
    QUICK_STATIC = {
      "*":      function(ctx) { return _query.tag("*", ctx); },
      "*:root": function() { return [_rootElement]; }, // fix #27 (*:root)
      ":root":  function() { return [_rootElement]; }, // fix #27 (*:root)
      "* :root":function() { return []; }, // fix #27b (* :root)
      "* html": function() { return []; }, // fix #27b (* html) IE6 CSS Star Hack
      html:     function() { return [_rootElement]; },
      head:     function() { return [_headElement]; },
      body:     function() { return [_doc.body]; },
      ":link":  function() { return _toArray(_doc.links); } // spoof
    },
    // :after :before :contains :digit :first-letter :first-line :link
    // :negative :playing :target :visited  !=  ?=  /=  <=  >=  &=  {  }
    REJECT_API  = /(:(a|b|co|dig|first-l|li|ne|p|t|v))|!=|\/=|<=|>=|&=|\{/, // }
    TRIM_QUOTE  = /^\s*["']?|["']?\s*$/g,
    QUICK_QUERY = /^(?:\*?(\.|#)([a-z_\u00C0-\uFFEE\-][\w\u00C0-\uFFEE\-]*)|(\w+)(?:\s*,\s*(\w+)(?:\s*,\s*(\w+))?)?|(\w+)\s+(\w+)(?:\s+(\w+))?)$/i,
    QUICK_HEAD  = /^#([a-z_\u00C0-\uFFEE\-][\w\u00C0-\uFFEE\-]*)\b(?![#\.:\[])|^((?:\.[a-z_\u00C0-\uFFEE\-][\w\u00C0-\uFFEE\-]*)+)$/i, // ]
    QUICK_COMMA = /^[^"'\(\)]*,/,
    ROOT_REJECT = /[a-z]+\-(child|type)$/,
    ID_OR_CLASS = /^[#\.]([a-z_\u00C0-\uFFEE\-][\w\u00C0-\uFFEE\-]*)/i,
    CHILD       = /^\s*(?:([>+~])\s*)?(\*|\w*)/,
    GROUP       = /^\s*,\s*/,
    PSEUDO      = /^(?::(not)\((?:(\*)|(\w+)|[#\.][a-z_\u00C0-\uFFEE\-][\w\u00C0-\uFFEE\-]*|\[\s*(?:[^~\^$*|=!\/\s]+\s*[~\^$*|!\/]?\=\s*(["'])?.*?\4i?|[^\]\s]+)\s*\]|:contains\((["'])?.*?\5\)|::?[\w\-]+(?:\([^\u0029]+\))?)\)|:contains\((["'])?(.*?)\6\)|::?([\w\-]+)(?:\((.*?)\))?)/i,
    ATTR        = /^\[\s*(?:([^~\^$*|=!\/\s]+)\s*([~\^$*|!\/]?\=)\s*(["'])?(.*?)\3(i?)|([^\]\s]+))\s*\]/,
    STYLE       = /^\{\s*([^\^$*=!<>&\/\s]+)\s*(:|[\^$*!<>&\/]?\=)\s*(["'])?(.*?)\3(i?)\s*\}/,
    NTH_ANB     = /^((even)|(odd)|(1n\+0|n\+0|n)|(\d+)|((-?\d*)n([+\-]?\d*)))$/,
    JOINT1      = { ">": 1, "+": 2, "~": 3 },
    JOINT2      = { "#": 1, ".": 2, ":": 3, "[": 4, "{": 5 }, // }]
    ATTR_ALIAS  = { "class": "className", "for": "htmlFor" },
    ATTR_IE_BUG = { href: 1, src: 1 },
    ATTR_OPERATOR = { "=": 1, "!=": 2, "*=": 3, "^=": 4,
                              "$=": 5, "~=": 6, "|=": 7, "/=": 8 },
    ATTR_CASESENS = { title: 0, id: 0, name: 0, "class": 0, "for": 0 },
    EX_PROP_KIND= { color: 1, backgroundColor: 1, opacity: 2, width: 3, height: 4,
                    left: 5, top: 5, right: 5, bottom: 5, backgroundImage: 6 },
    EX_OPERATOR = { ":": 1,     // E{prop : value}             match
                    "=": 1,     // E{prop = value}             match
                    "!=": 2,    // E{prop != value}            not match
                    "*=": 3,    // E{prop *= value}            match somewhere
                    "^=": 4,    // E{prop ^= value}            match head
                    "$=": 5,    // E{prop $= value}            match tail
                    "/=": 8,    // E{prop /= "value"}          match Regexp
                    ">=": 9,    // E{prop >= value}            more than
                    "<=": 10,   // E{prop <= value}            less than
                    "&=": 11    // E{prop &= value1 ~ value2}  from value1 to value2
                  },
    DUMMY = function() { return []; },
    filters = {
      "first-child":    [0x01, childFilter],
      "last-child":     [0x02, childFilter],
      "only-child":     [0x03, childFilter],
      "nth-child":      [0x04, nthChildFilter],
      "nth-last-child": [0x05, nthChildFilter],
      "nth-of-type":    [0x06, nthOfTypeFilter],
      "nth-last-of-type":
                        [0x07, nthOfTypeFilter],
      "first-of-type":  [0x09, ofTypeFilter],
      "last-of-type":   [0x0a, ofTypeFilter],
      enabled:          [0x0b, simpleFilter],
      disabled:         [0x0c, simpleFilter],
      checked:          [0x0d, simpleFilter],
      link:             [0x0e, _visited ? visitedFilter : link],
      visited:          [0x0f, _visited ? visitedFilter : DUMMY],
      hover:            [0x10, actionFilter],
      focus:            [0x11, actionFilter],
      empty:            [0x12, empty],
      lang:             [0x13, lang],
      "only-of-type":   [0x14, onlyOfType],
      // [0x15] reserved.
      root:             [0x16, root],
      target:           [0x17, target],
      contains:         [0x18, contains],
      digit:            [0x40, extendFilter],
      negative:         [0x41, extendFilter],
      tween:            [0x42, extendFilter],
      playing:          [0x43, extendFilter],
      // bit information
      //    0x100: use flag
      //    0x200: none operation flag
      //    0x400: :not exclude flag
      //    0x800: need double-semicolon(::) flag
      before:           [0xf00, null],
      after:            [0xf00, null],
      "first-letter":   [0xf00, null],
      "first-line":     [0xf00, null]
    };

if (_visited) {
  delete QUICK_STATIC[":link"];
}

// uuQuery - query css
_query = function(expr,      // @param String: "css > rule"
                  context) { // @param Node(= document): query context
                             // @return NodeArray( [Node, Node, ...] )
                             //         /EmptyArray( [] )
  if (_doc.querySelectorAll) {
    if (!REJECT_API.test(expr)) {
      try {
        return _toArray((context || _doc).querySelectorAll(expr));
      } catch(err) {} // case: extend pseudo class / operators
    }
  }
  _uid2node = _mm.uid2node.refHash();
  return querySelectorAll(expr.replace(/^\s+|\s+$/g, ""), context || _doc);
};

// uuQuery.id - query id
_query.id = function(expr) { // @param String: id
                             // @return Node/null
  return _doc.getElementById(expr);
};

// uuQuery.tag - query tagName
_query.tag = (function() {
  if (!_ie) {
    return function(expr,      // @param String: "*" or "tag"
                    context) { // @param Node(= document): query context
                               // @return NodeArray( [Node, Node, ...] )
                               //         /EmptyArray( [] )
      return Array.prototype.slice.call(
                  (context || _doc).getElementsByTagName(expr));
    }
  }
  return function(expr, context) {
    var nodeList = (context || _doc).getElementsByTagName(expr),
        rv = [], ri = -1, v, i = 0;
    if (expr !== "*") {
      while ( (v = nodeList[i++]) ) {
        rv[++ri] = v;
      }
    } else { // ie: getElementsByTagName("*") has comment nodes
      while ( (v = nodeList[i++]) ) {
        (v.nodeType === 1) && (rv[++ri] = v);
      }
    }
    return rv;
  };
})();

// uuQuery.className - query className
_query.className = (function() {
  if (_doc.getElementsByClassName) {
    return function(expr,      // @param JointString: "class", "class1, ..."
                    context) { // @param Node(= document): query context
                               // @return NodeArray( [Node, Node, ...] )
                               //         /EmptyArray( [] )
      return Array.prototype.slice.call(
                  (context || _doc).getElementsByClassName(expr));
    };
  }
  return function(expr, context) {
    var nodeList = (context || _doc).getElementsByTagName("*"),
        name = expr.replace(/^\s+|\s+$/g, "").split(/\s+/),
        rv = [], ri = -1, v, match, c, i = 0, nz = name.length, rex,
        urv = [], uri = -1, unq = {}, u = 0;

    if (nz > 1) { // #fix 170b
      while ( (v = name[u++]) ) {
        if (!(v in unq)) {
          unq[v] = 1;
          urv[++uri] = v;
        }
      }
      name = urv, nz = uri + 1;
    }
    // /(?:^| )(AA|BB|CC)(?:$|(?= ))/g
    rex = RegExp("(?:^| )(" + name.join("|") + ")(?:$|(?= ))", "g");

    while ( (v = nodeList[i++]) ) {
      c = v.className;
      if (c) {
        match = c.match(rex); // NG: match = rex.exec(c);
        (match && match.length >= nz) && (rv[++ri] = v);
      }
    }
    return rv;
  };
})();

function quickQuery(expr, match, context) {
  var rv = [], ri = -1, unq = {}, uid, newid,
      m1, m2, m3, nodeList1, nodeList2, nodeList3,
      v, i, j, k, iz, jz, kz;

  // "#id" or ".class"
  if (match[1]) {
    if (match[1] === ".") {
      return _query.className(match[2], context);
    }
    nodeList1 = (context.ownerDocument || _doc).getElementById(match[2]);
    return nodeList1 ? [nodeList1] : [];
  }

  // "E" or "E,F" or "E,F,G"
  if (match[3]) {
    m1 = match[3], m2 = match[4], m3 = match[5];
    if (/^\d+$/.test(m1) || /^\d+$/.test(m2) || /^\d+$/.test(m3)) {
      throw expr + " syntax error";
    }

    unq[m1] = 1, nodeList1 = _toArray(context.getElementsByTagName(m1));
    if (m2 && !(m2 in unq)) {
      unq[m2] = 1, nodeList2 = _toArray(context.getElementsByTagName(m2));
    }
    if (m3 && !(m3 in unq)) {
      unq[m3] = 1, nodeList3 = _toArray(context.getElementsByTagName(m3));
    }
    return [].concat(nodeList1, nodeList2 || [], nodeList3 || []);
  }

  // "E F" or "E F G"
  m1 = match[6], m2 = match[7], m3 = match[8];

  nodeList1 = context.getElementsByTagName(m1); // "E"
  for (i = 0, iz = nodeList1.length; i < iz; ++i) {
    nodeList2 = nodeList1[i].getElementsByTagName(m2); // "E F"
    for (j = 0, jz = nodeList2.length; j < jz; ++j) {
      if (m3) {
        nodeList3 = nodeList2[j].getElementsByTagName(m3); // "E F G"
        for (k = 0, kz = nodeList3.length; k < kz; ++k) {
          v = nodeList3[k];
          uid = v[UNIQUEID] ||
              (_uid2node[v[UNIQUEID] = newid = ++_win[UNIQUEID]] = v, newid);
          if (!(uid in unq)) {
            rv[++ri] = v;
            unq[uid] = 1;
          }
        }
      } else {
        v = nodeList2[j];
        uid = v[UNIQUEID] ||
            (_uid2node[v[UNIQUEID] = newid = ++_win[UNIQUEID]] = v, newid);
        if (!(uid in unq)) {
          rv[++ri] = v;
          unq[uid] = 1;
        }
      }
    }
  }
  return rv;
}

function querySelectorAll(expr, context) {
  var _contentType, _tags, // alias
      // --- double registration guard ---
      uid,        // unique-id
      guard = {}, // global guard
      unq   = {}, // local guard
      mixed = 0,  // 1: mixed
      // --- result and context elements ---
      rv  = [], ri, r,
      ctx = [context],
      // --- loop out flag --
      lastExpr1,  // last expr for outer loop
      lastExpr2,  // last expr for inner loop
      // --- iterator and loop counter ---
      i, j, iz,
      // --- work ---
      withComma = expr.indexOf(",") >= 0, // with comma(",")
      tag,        // the E or F or universal selector("*")
      isUniversal,// true: tag is universal selector("*")
      joint,      // >+~_#.:[     // ]
      nodeList, needle, pseudo, v, w, operator, match, negate = 0;

  if (/^[>+~]|[>+~*]{2}|[>+~]$/.test(expr)) {
    throw expr + " syntax error";
  }

  // --- Quick phase ---
  if (!withComma && expr in QUICK_STATIC) { // "*" ":root" "body" ...
    return QUICK_STATIC[expr](context);
  }
  if ( (match = QUICK_QUERY.exec(expr)) ) { // "#id" ".class" "E" "E F" "E,F"...
    return quickQuery(expr, match, context);
  }
  if (withComma && QUICK_COMMA.test(expr)) { // split("#id, .class, E")
    w = expr.split(","); // "expr, expr, expr"
    for (i = 0, iz = w.length; i < iz; ++i) {
      v = w[i].replace(/^\s+|\s+$/g, "");
      if (!v) {
        throw expr + " syntax error";
      }
      r = querySelectorAll(v, context);
      mixin(r, rv, unq);
    }
    return rv;
  }
  if (!withComma) {
    if ( (match = QUICK_HEAD.exec(expr)) ) {
      if (match[1]) {
        w = _doc.getElementById(match[1]);
        ctx = w ? [w] : [];
      } else {
        v = match[2].replace(/\./g, " "); // ".class.class" -> " class class"
        return _query.className(v, context);
      }
      expr = expr.slice(match[0].length);
    }
  }

  // init tag set
  uid = _mm.uid(context);
  _contentType = _contentTypeCache[uid] ||
                    (_contentTypeCache[uid] = getContentType(context));
  _tags = _contentType === 1 ? _htmlTag : _xmlTag;

  // --- Generic phase ---
  while (expr && expr !== lastExpr1) { // outer loop
    lastExpr1 = expr;

    r = [], ri = -1, unq = {}, i = 0, iz = ctx.length;

    // "E > F"  "E + F"  "E ~ F"  "E"  "E F" phase
    if ( (match = CHILD.exec(expr)) ) {
      tag = match[2];
      tag = tag ? (_tags[tag] || addTag(tag, _contentType)) : "*";
      isUniversal = tag === "*"; // true: tag is universal selector

      if (match[1]) {
        joint = JOINT1[match[1]];

        if (joint === 1) { // 1: "E > F"
          for (; i < iz; ++i) {
            for (v = ctx[i].firstChild; v; v = v.nextSibling) {
              if (v.nodeType === 1) {
                if (isUniversal || v.tagName === tag) {
                  r[++ri] = v;
                }
              }
            }
          }
        } else if (joint === 2) { // 2: "E + F"
          for (; i < iz; ++i) {
            for (v = ctx[i].nextSibling; v; v = v.nextSibling) {
              if (v.nodeType === 1) {
                w = v.tagName;
                if (_ie && !w.indexOf("/")) { continue; } // fix #25
                if (isUniversal || w === tag) {
                  r[++ri] = v;
                }
                break;
              }
            }
          }
        } else { // 3: "E ~ F"
          for (; i < iz; ++i) {
            for (v = ctx[i].nextSibling; v; v = v.nextSibling) {
              if (v.nodeType === 1) {
                if (isUniversal || v.tagName === tag) {
                  uid = v[UNIQUEID] ||
                        (_uid2node[v[UNIQUEID] = w = ++_win[UNIQUEID]] = v, w);
                  if (uid in unq) {
                    break;
                  } else {
                    r[++ri] = v;
                    unq[uid] = 1;
                  }
                }
              }
            }
          }
        }
      } else {
        // >+~ is not found
        if (iz === 1) {
          r = _query.tag(tag, ctx[0]);
        } else {
          for (; i < iz; ++i) {
            nodeList = ctx[i].getElementsByTagName(tag);

            // tag("*") has text/comment node(in IE)
            j = 0;
            while ( (v = nodeList[j++]) ) {
              if (!_ie || !isUniversal || v.nodeType === 1) {
                if (isUniversal || v.tagName === tag) {
                  uid = v[UNIQUEID] ||
                        (_uid2node[v[UNIQUEID] = w = ++_win[UNIQUEID]] = v, w);
                  if (!(uid in unq)) {
                    r[++ri] = v;
                    unq[uid] = 1;
                  }
                }
              }
            }
          }
        }
      }
      ctx = r;
      expr = expr.slice(match[0].length);
    }

    // Attribute, Class, Pseudo, ID phase
    while (expr && expr !== lastExpr2) { // inner loop
      lastExpr2 = expr;
      match = null;

      r = [], ri = -1, i = 0;

      joint = JOINT2[expr.charAt(0)] || 9; // 9: dummy

      switch (joint) {
      case 1: // 1: "#id"
        if ( (match = ID_OR_CLASS.exec(expr)) ) {
          needle = match[1]; // "id"

          if (_contentType === 1) { // 1:html (match id or name)
            while ( (v = ctx[i++]) ) {
              if (((w = v.id || v.name) && (w === needle)) ^ negate) {
                r[++ri] = v;
              }
            }
          } else { // 2: xml (match id)
            while ( (v = ctx[i++]) ) {
              if (((w = v.id) && (w === needle)) ^ negate) {
                r[++ri] = v;
              }
            }
          }
        }
        break;

      case 2: // 2: ".class"
        if ( (match = ID_OR_CLASS.exec(expr)) ) {
          needle = (" " + match[1] + " "); // " className "

          while ( (v = ctx[i++]) ) {
            if (((w = v.className) &&
                ((" " + w + " ").indexOf(needle) >= 0)) ^ negate) {
              r[++ri] = v;
            }
          }
        }
        break;

      case 3: // 3: pseudo
        if ( (match = PSEUDO.exec(expr)) ) {
          if ( (iz = ctx.length) ) {
            if (match[1]) { // :not(...)
              if (negate) {
                throw ":not(:not(...)) syntax error";
              }
              if (match[2]) { // :not(*)
                break;
              }
              if (match[3]) { // ':not(div)' -> match[3] = "div"
                tag = match[3];
                tag = _tags[tag] || addTag(tag, _contentType);
                while ( (v = ctx[i++]) ) {
                  (v.tagName !== tag) && (r[++ri] = v);
                }
                break;
              }
              w = expr.slice(match[0].length); // remain expr
              expr = match[0].replace(/^:not\(\s*|\s*\)$/g, "") + w;
              ++negate;
              continue;
            } else {
              pseudo = match[8] || "contains";

              // ":root:xxx-child" or ":root:xxx-type" -> not match
              // ":root:not(:first-child)"             -> match root element
              if (iz === 1 && ctx[0] === _rootElement
                           && ROOT_REJECT.test(pseudo)) {
                r = negate ? [_rootElement] : [];
              } else {
                if ( !(v = filters[pseudo]) ) {
                  throw ":" + pseudo + " unsupported";
                }
                w = v[0];
                if (w & 0x100) {
                  if ((w & 0x800) && !/^::/.test(expr)) {
                    throw match[0] + " syntax error(need ::)";
                  }
                  if ((w & 0x400) && negate) {
                    throw ":not(" + match[0] + ") syntax error" +
                          "(exclude pseudo-element)";
                  }
                  if (w & 0x200) { // 0x100 is none operation
                    r = negate ? [] : ctx;
                    break;
                  }
                }
                r = v[1].call(this, w, negate, ctx, pseudo,
                              match[9] || match[7], _tags, _contentType);
              }
            }
          }
        }
        break;

      case 4: // 4: Attr "[A=V]" or "[A]"
        if ( (match = ATTR.exec(expr)) ) {
          if (match[6]) { // "[A]"
            needle = match[6];

            while ( (v = ctx[i++]) ) {
              if (_ie) {
                w = v.getAttributeNode(needle);
                if ((w && w.specified) ^ negate) {
                  r[++ri] = v;
                }
              } else if (v.hasAttribute(needle) ^ negate) {
                r[++ri] = v;
              }
            }
          } else { // "[A=V]"
            // fix #Acid2 [class=second two]
            if (!match[3] &&
                match[4].indexOf(" ") >= 0 &&
                match[4].replace(/\\ /, "").indexOf(" ") >= 0) {
              throw match[0] + " syntax error";
            }
            needle = match[4].replace(/^\s*["']|["']\s*$/g, "");
            operator = ATTR_OPERATOR[match[2]];
            if (!operator) {
              throw match[0] + " unsupported";
            }
            w = match[5] || ""; // regexp flag

            if (operator === 8) { // 8: "/=" is regexp operator
              needle = RegExp(needle, w);
            } else {
              // fix [class=i] -> match[4] = "", match[5] = "i"
              w && (needle += w);
            }
            r = judgeAttr(negate, ctx, match[1], operator, needle);
          }
        }
        break;

      case 5: // 5: Style "{S=V}" or "{S}"
        if (_win.uuStyle) {
          if ( (match = STYLE.exec(expr)) ) {
            r = styleQuery(negate, ctx, match);
          }
        }
      }

      if (match) {
        ctx = r;
        expr = expr.slice(match[0].length);
        negate = 0;
      }
    }

    // "E,F" phase
    if (withComma && expr && (match = GROUP.exec(expr)) ) {
      ++mixed;
      mixin(ctx, rv, guard);
      ctx = [context];
      lastExpr1 = lastExpr2 = "";
      expr = expr.slice(match[0].length);
    }
  }

  if (expr.length) {
    throw expr + " unsupported";
  }
  return mixed ? mixin(ctx, rv, guard) : ctx;
}

// mix results
function mixin(ctx, rv, guard) {
  var ri = rv.length - 1, i = 0, v, uid, newid;

  while ( (v = ctx[i++]) ) {
    uid = v[UNIQUEID] ||
          (_uid2node[v[UNIQUEID] = newid = ++_win[UNIQUEID]] = v, newid);
    if (!(uid in guard)) {
      rv[++ri] = v;
      guard[uid] = 1;
    }
  }
  return rv;
}

function getContentType(context) {
  var owner = context.ownerDocument || _doc,
      p = owner.createElement("p"),
      P = owner.createElement("P");
  // see http://d.hatena.ne.jp/uupaa/20081010/1223630689 [THX! id:os0x]
  return p.tagName === P.tagName ? 1 : 2; // 1: HTMLDocument, 2: XMLDocument
}

function addTag(tag, contentType) {
  var lo = tag.toLowerCase(),
      up = tag.toUpperCase();
  if (!(lo in _htmlTag)) {
    _xmlTag[up] = _htmlTag[lo] = _htmlTag[up] = up;
    _xmlTag[lo] = lo;
  }
  return contentType === 1 ? up : tag;
}

// [attr operator "value"]
function judgeAttr(negate, elms, attr, operator, value) {
  var rv = [], ri = -1, r, e, v = value, i = 0, rex,
      attrFlag = 0, // attrFlag: ie only
      isInsens = !(attr in ATTR_CASESENS); // true: case insensitive

  if (_ie) {
    if (_iemode8 || ATTR_IE_BUG[attr]) { // fix a[href^="#"]
      attrFlag = 2;
    } else {
      attr = ATTR_ALIAS[attr] || attr;
    }
  }

  if (operator < 3) { // [attr = value] or [attr != value]
    --operator;
    v = v.replace(/\\/, ""); // fix #Acid2 [class=second\ two]
    if (isInsens) {
      v = v.toLowerCase();
    }
    while ( (e = elms[i++]) ) {
      if ( (r = e.getAttribute(attr, attrFlag)) ) {
        if (isInsens) {
          r = (r + "").toLowerCase();
        }
        ((v == r) ^ operator ^ negate) && (rv[++ri] = e);
      }
    }
  } else {
    switch (operator) {
    case 3: rex = v; break;                           // [attr *= value]
    case 4: rex = "^" + v; break;                     // [attr ^= value]
    case 5: rex = v + "$"; break;                     // [attr $= value]
    case 6: if (v.indexOf(" ") >= 0) { return rv; }   // fix #7b
            rex = "(?:^| )" + v + "(?:$| )"; break;   // [attr ~= value]
    case 7: rex = "^" + v + "\\-|^" + v + "$"; break; // [attr |= value]
    }
    if (rex) {
      v = RegExp(rex, isInsens ? "i": "");
    }
    while ( (e = elms[i++]) ) {
      r = e.getAttribute(attr, attrFlag);
      if ((r && v.test(r)) ^ negate) {
        rv[++ri] = e;
      }
    }
  }
  return rv;
}

// :first-child  :last-child  :only-child
function childFilter(fid, negate, elms) {
  var rv = [], ri = -1, i = 0, v, c, f,
      iter1 = "previousSibling",
      iter2 = "nextSibling";

  while ( (v = elms[i++]) ) {
    f = 0;
    // first-child
    if (fid & 1) {
      for (c = v[iter1]; c; c = c[iter1]) {
        if (c.nodeType === 1) {
          ++f;
          break;
        }
      }
    }
    // last-child
    if (!f && fid & 2) {
      for (c = v[iter2]; c; c = c[iter2]) {
        if (c.nodeType === 1) {
          ++f;
          break;
        }
      }
    }
    if ((!f) ^ negate) {
      rv[++ri] = v;
    }
  }
  return rv;
}

// :nth-child  :nth-last-child
function nthChildFilter(fid, negate, elms, pseudo, value, tags, contentType) {
  if (value === "n") {
    return negate ? [] : elms;
  }
  // 0x4 = nth-child, 0x5 = nth-last-child
  var v = elms[0].tagName,
      tag = tags[v] || addTag(v, contentType),
      rv = [], ri = -1, i = 0, iz = elms.length, uid, newid, unq = {},
      pn, cn, idx, ok,
      iter1 = (fid === 0x5) ? "lastChild" : "firstChild",
      iter2 = (fid === 0x5) ? "previousSibling" : "nextSibling",
      f = nth(value), a = f.a, b = f.b, k = f.k;

  for (; i < iz; ++i) {
    pn = elms[i].parentNode;
    uid = pn[UNIQUEID] ||
          (_uid2node[pn[UNIQUEID] = newid = ++_win[UNIQUEID]] = pn, newid);
    if (!(uid in unq)) {
      unq[uid] = 1;
      idx = 0;

      for (cn = pn[iter1]; cn; cn = cn[iter2]) {
        if (cn.nodeType === 1) {
          ++idx;

          ok = 0;
          switch (k) {
          case 1:  ok = (idx === b); break;
          case 2:  ok = (idx >= b); break;
          case 3:  ok = (!((idx - b) % a) && (idx - b) / a >= 0); break;
          default: ok = (idx <= b);
          }
          (ok ^ negate) && cn.tagName === tag && (rv[++ri] = cn);
        }
      }
    }
  }
  return rv;
}

// :nth-of-type  :nth-last-of-type
function nthOfTypeFilter(fid, negate, elms, pseudo, value) {
  if (fid === 0x07) { // 0x07: nth-last-of-type
    elms.reverse();
  }

  var rv = [], ri = -1, v, i = 0, unq = {},
      idx, pn, currentParent = null, tagName, ok,
      f = nth(value), a = f.a, b = f.b, k = f.k;

  while ( (v = elms[i++]) ) {
    pn = v.parentNode;
    if (pn !== currentParent) {
      currentParent = pn;
      unq = {};
    }

    tagName = v.tagName;
    if (tagName in unq) {
      ++unq[tagName];
    } else {
      unq[tagName] = 1;
    }
    idx = unq[tagName];
    ok = 0;

    switch (k) {
    case 1:  ok = (idx === b); break;
    case 2:  ok = (idx >=  b); break;
    case 3:  ok = (!((idx - b) % a) && (idx - b) / a >= 0); break;
    default: ok = (idx <=  b);
    }
    if (ok ^ negate) {
      rv[++ri] = v;
    }
  }
  return rv;
}

// :first-of-type  :last-of-type
function ofTypeFilter(fid, negate, elms) {
  if (fid === 0x0a) { // 0x0a: last-of-type
    elms.reverse();
  }
  var rv = [], ri = -1, v, i = 0, unq = {},
      pn, currentParent = null;

  while ( (v = elms[i++]) ) {
    pn = v.parentNode;
    if (pn !== currentParent) {
      currentParent = pn;
      unq = {};
    }
    if (v.tagName in unq) {
      ++unq[v.tagName];
    } else {
      unq[v.tagName] = 1;
    }
    if ((unq[v.tagName] === 1) ^ negate) {
      rv[++ri] = v;
    }
  }
  return rv;
}

// :enabled  :disabled  :checked
function simpleFilter(fid, negate, elms) {
  var rv = [], ri = -1, v, i = 0, ok, needValidate,
      rex = /^(input|button|select|option|textarea)$/i;

  while ( (v = elms[i++]) ) {
    needValidate = ok = 0;
    switch (fid) {
    case 0x0b: ++needValidate; ok = !v.disabled; break;  // 0x0b: enabled
    case 0x0c: ++needValidate; ok = !!v.disabled; break; // 0x0c: disabled
    case 0x0d: ++needValidate; ok = !!v.checked; break;  // 0x0d: checked
    }

    if (needValidate && !rex.test(v.tagName)) { // fix #144
      if (negate) {
        rv[++ri] = v;
      }
    } else if (ok ^ negate) {
      rv[++ri] = v;
    }
  }
  return rv;
}

// :root
function root(fid, negate, elms) {
  if (!negate) {
    return [_rootElement];
  }
  var rv = [], ri = -1, v, i = 0;
  while ( (v = elms[i++]) ) {
    if (v !== _rootElement) {
      rv[++ri] = v;
    }
  }
  return rv;
}

// :target
function target(fid, negate, elms, pseudo, value, tags, contentType) {
  var rv = [], ri = -1, i = 0, v, needle = location.hash.slice(1);

  if (needle) {
    if (contentType === 1) { // 1: html
      while ( (v = elms[i++]) ) {
        (((v.id || v.name) === needle) ^ negate) && (rv[++ri] = v);
      }
    } else { // 2: xml
      while ( (v = elms[i++]) ) {
        ((v.id === needle) ^ negate) && (rv[++ri] = v);
      }
    }
  }
  return rv;
}

// :contains
function contains(fid, negate, elms, pseudo, value) {
  valie = value.replace(TRIM_QUOTE, "");
  var rv = [], ri = -1, v, i = 0;

  while ( (v = elms[i++]) ) {
    if ((v[_innerText].indexOf(value) >= 0) ^ negate) {
      rv[++ri] = v;
    }
  }
  return rv;
}

// :link
function link(fid, negate, elms) {
  var rv = [], ri = -1, ary = _toArray(_doc.links), v, i = 0,
      j = 0, jz = elms.length, hit;
  while ( (v = ary[i++]) ) {
    for (hit = -1, j = 0; j < jz; ++j) {
      if (elms[j] === v) {
        hit = j;
        break;
      }
    }
    if ((hit >= 0) ^ negate) {
      rv[++ri] = v;
    }
  }
  return rv;
}

// :empty
function empty(fid, negate, elms) {
  var rv = [], ri = -1, i = 0, v, c, missMatch = 0;
  while ( (v = elms[i++]) ) {
    missMatch = 0;
    for (c = v.firstChild; c; c = c.nextSibling) {
      if (c.nodeType === 1) {
        ++missMatch;
        break;
      }
    }
    // touch(v.textContent) very slowly
    if ((!missMatch && !v[_innerText]) ^ negate) {
      rv[++ri] = v;
    }
  }
  return rv;
}

// :lang
function lang(fid, negate, elms, pseudo, value) {
  var rv = [], ri = -1, v, i = 0, iz = elms.length,
      rex = RegExp("^(" + value + "$|" + value + "-)", "i");

  for (; i < iz; ++i) { // don't touch me
    v = elms[i];
    while (v && v !== _doc && !v.getAttribute("lang")) {
      v = v.parentNode;
    }
    if (((v && v !== _doc) && rex.test(v.getAttribute("lang"))) ^ negate) {
      rv[++ri] = elms[i];
    }
  }
  return rv;
}

// :only-of-type
function onlyOfType(fid, negate, elms, pseudo, value, tags, contentType) {
  var rv = [], ri = -1, v, i = 0, c, f, t, tagName,
      iter1 = "nextSibling",
      iter2 = "previousSibling";

  while ( (v = elms[i++]) ) {
    f = 0;
    tagName = v.tagName,
    t = tags[tagName] || addTag(tagName, contentType);
    for (c = v[iter1]; c; c = c[iter1]) {
      if (c.nodeType === 1 && c.tagName === t) {
        ++f;
        break;
      }
    }
    if (!f) {
      for (c = v[iter2]; c; c = c[iter2]) {
        if (c.nodeType === 1 && c.tagName === t){
          ++f;
          break;
        }
      }
    }
    if ((!f) ^ negate) {
      rv[++ri] = v;
    }
  }
  return rv;
}

// parse :nth-xxx(an+b)
function nth(anb) {
  var a, b, c, match = NTH_ANB.exec(anb);

  if (!match) { throw anb + " unsupported"; }
  if (match[2]) { return { a: 2, b: 0, k: 3 }; } // nth(even)
  if (match[3]) { return { a: 2, b: 1, k: 3 }; } // nth(odd)
  if (match[4]) { return { a: 0, b: 0, k: 2 }; } // nth(1n+0), nth(n+0), nht(n)
  if (match[5]) { return { a: 0, b: _int(match[5], 10), k: 1 }; } // nth(1)
  a = (match[7] === "-" ? -1 : match[7] || 1) - 0;
  b = (match[8] || 0) - 0;
  c = a < 2;
  return {
    a: c ? 0 : a,
    b: b,
    k: c ? a + 1 : 3
  };
}

// === uuQuery Selectors ==================================
// :digit(0x40)  :negative(0x41)  :tween(0x42)  :playing(0x43)
function extendFilter(fid, negate, elms) {
  var rv = [], ri = -1, v, i = 0, ok,
      DIGIT = /^\s*(?:[\-+]?)[\d,\.]+\s*$/,
      NEGATIVE = /^\s*\-[\d,\.]+\s*$/;

  while ( (v = elms[i++]) ) {
    ok = 0;
    switch (fid) {
    case 0x40: ok = DIGIT.test(v[_innerText] || ""); break;
    case 0x41: ok = NEGATIVE.test(v[_innerText] || ""); break;
    case 0x42: ok = !!v.tween; break;
    case 0x43: ok = v.tween && v.tween.playing(); break;
    }
    if (ok ^ negate) {
      rv[++ri] = v;
    }
  }
  return rv;
}

function parseColor(color) { // @param HexColorString: "#000000" style only
                             // @return Number: color number
  var rv = color;
  if (typeof rv === "string") {
    if (_win.uuColor) {
      rv = uuColor.parse(rv)[0];
      rv = _int(rv.replace(/^#/, "0x"), 16);
    }
  }
  return rv;
}

function styleQuery(negate, elms, match) {
  var value = match[4].replace(/^\s*["']|["']\s*$/g, ""), // trim quote
      _style = uuStyle,
      prop, propKind,
      operator = EX_OPERATOR[match[2]], w,
      rv = [], ri = -1, ary, ok, r, e, v1, v2 = 0, i = 0,
      hasRange = 0, unitExchanged = 0;

  if (!operator) {
    throw match[0] + " unsupported";
  }
  w = match[5] || ""; // regexp flag

  if (operator === 8) { // 8: "/=" is regexp operator
    value = RegExp(value, w); // build regexp object
  } else {
    // fix {class=i} -> match[4] = "", match[5] = "i"
    w && (value += w);
  }

  prop = match[1];

  v1 = value;
  if (operator === 11) { // 11: "&="
    ary = v1.split(/\s*\~\s*/); // {prop&=0x0~0xf}
    if (ary.length !== 2) {
      throw "[" + prop + "&=" + v1 + "-???] syntax error";
    }
    v1 = ary[0];
    v2 = ary[1];
    hasRange = 1;
  }

  // pre-filter
  propKind = EX_PROP_KIND[prop] || 0;
  switch (propKind) {
  case 1: // 1: color, backgroundColor to number
    v1 = parseColor(v1);
    hasRange && (v2 = parseColor(v2));
    break;
  case 2: // 2: opacity
    v1 = _float(v1);
    hasRange && (v2 = _float(v2));
  }

  // range normalize
  if (hasRange && v1 > v2) { // {prop&=1~0} -> {prop&=0~1}
    r = v2, v2 = v1, v1 = r; // swap(v1, v2);
  }

  while ( (e = elms[i++]) ) {
    switch (propKind) {
    case 1: // color, backgroundColor
      r = parseColor((_ie ? e[_runstyle]
                          : _runstyle(e, ""))[prop]);
      break;
    case 2: // opacity
      r = _ie ? (e.filters.alpha ? e.style.opacity : 1.0)
              : _float(_runstyle(e, "").opacity);
      break;
    case 3: // width
      r = _style.getPixel(e, "width");

      if (!unitExchanged) {
        ++unitExchanged;
        v1 = _style.toPixel(e, v1);
        hasRange && (v2 = _style.toPixel(e, v1));
      }
      break;
    case 4: // height
      r = _style.getPixel(e, "height");

      if (!unitExchanged) {
        ++unitExchanged;
        v1 = _style.toPixel(e, v1);
        hasRange && (v2 = _style.toPixel(e, v1));
      }
      break;
    case 5: // top, right, bottom, left
      r = _style.toPixel(e, (_ie ? e[_runstyle]
                                  : _runstyle(e, ""))[prop]);
      if (!unitExchanged) {
        ++unitExchanged;
        v1 = _style.toPixel(e, v1);
        hasRange && (v2 = _style.toPixel(e, v1));
      }
      break;
    case 6: // backgroundImage
      r = _style.getBGImg(e) || "none"; // "http://..." or "none"
      break;
    default:
      w = (_ie ? e[_runstyle]
               : _runstyle(e, ""))[prop];
      r = (operator >= 9) ? _float(w) : w;
    }

    ok = 0;
    switch (operator) {
    case 1: ok = v1 == r; break;            // {prop = value} or {prop: value}
    case 2: ok = v1 != r; break;            // {prop != value}
    case 3: ok = r.indexOf(v1) >= 0; break; // {prop *= value}
    case 4: ok = !r.indexOf(v1); break;     // {prop ^= value}
    case 5: ok = (r.lastIndexOf(v1) + v1.length) === r.length; break;
                                            // {prop $= value}
    case 8: ok = v1.test(r); break;         // {prop /= "regexp"ig}
    case 9: ok = r >= v1; break;            // {prop >= value}
    case 10: ok = r <= v1; break;           // {prop <= value}
    case 11: ok = r >= v1 && r <= v2;       // {prop &= #000000~#ffffff}
    }
    if (ok ^ negate) {
      rv[++ri] = e;
    }
  }
  return rv;
}

// === Action Selectors ====================================
function visitedFilter(fid, negate, elms) {
  // :link(0x0e)  :visited(0x0f)
  var rv = [], ri = -1, v, i = 0, ok, cs, idx;

  // http://d.hatena.ne.jp/uupaa/20080928/1222543331
  _ss.create(_ssid);
  idx = _ss.insertRule(_ssid, "a:visited", _ie ? "ruby-align:center"
                                               : "outline:0 solid #000");
  while ( (v = elms[i++]) ) {
    if (v.tagName === "A") {
      if (_ie) {
        ok = (v.currentStyle.rubyAlign === "center") ? 1 : 0;
      } else {
        cs = _runstyle(v, "");
        ok = (cs.outlineWidth === "0px" &&
              cs.outlineStyle === "solid") ? 1 : 0;
      }
      if (fid === 0x0e) {
        if ((!ok) ^ negate) {
          rv[++ri] = v;
        }
      } else {
        if (ok ^ negate) {
          rv[++ri] = v;
        }
      }
    }
  }
  _ss.removeRule(_ssid, idx);
  return rv;
}

function actionFilter(fid, negate, elms, pusedo) {
  // :hover(0x10)  :focus(0x11)
  var rv = [], ri = -1, v, i = 0, ok, cs, idx;

  // http://d.hatena.ne.jp/uupaa/20080928/1222543331
  _ss.create(_ssid);
  idx = _ss.insertRule(_ssid, ":" + pusedo, _ie ? "ruby-align:center"
                                                : "outline:0 solid #000");
  while ( (v = elms[i++]) ) {
    if (_ie) {
      ok = (v.currentStyle.rubyAlign === "center") ? 1 : 0;
    } else {
      cs = _runstyle(v, "");
      ok = (cs.outlineWidth === "0px" &&
            cs.outlineStyle === "solid") ? 1 : 0;
    }
    if (ok ^ negate) {
      rv[++ri] = v;
    }
  }
  _ss.removeRule(_ssid, idx);
  return rv;
}

// --- initialize ---
(function() {
  // create tag dict.
  var ary = ("*,div,p,a,ul,ol,li,span,td,tr,dl,dt,dd,h1,h2,h3,h4," +
             "iframe,form,input,textarea,select,body,style,script").split(","),
      i = 0, iz = ary.length;
  for (; i < iz; ++i) {
    addTag(ary[i]);
  }
})();

// --- export ---
_win.uuQuery = _query;            // window.uuQuery
_query.filters = filters;         // window.uuQuery.filters
_query.childFilter = childFilter; // window.uuQuery.childFilter

})(); // uuQuery scope


// === uuCanvas ===
// depend: uuMeta, uuColor, uuStyle, uuImage
/*
uuCanvas.init(canvas, vml = false) - return new canvas element
uuCanvas.ready(callback)
uuCanvas.already() - return true is already
uuCanvas.expire()
uuCanvas.SL2D
uuCanvas.VML2D
 */
(function() {
var _canvas, // inner namespace
    _mm = uuMeta,
    _style = uuStyle,
    _image = uuImage,
    _win = window,
    _doc = document,
    _ie = _mm.ie,
    _opera = _mm.opera,
    _gecko = _mm.gecko,
    _webkit = _mm.webkit,
    _chrome = _mm.chrome,
    _slver = _mm.slver,
    _uaver = _mm.uaver,
    _egver = _mm.enginever,
    _int = parseInt,
    _float = parseFloat,
    _math = Math,
    _round = _math.round,
    _ceil = _math.ceil,
    _sin = _math.sin,
    _cos = _math.cos,
    _max = _math.max,
//  _rad = _math.PI / 180, // Math.toRadians
    _deg = 180 / _math.PI, // Math.toDegrees
    _runstyle = _mm.runstyle,
    _mix = _mm.mix,
    _hex = _mm.hex,
    _uid = 0, // cache uid
    // ---
    _canvasReady = 0,
    _crc2d, // lazy
    _metric, // Text Metric Element
    _matrix,
    _zoom = 10,
    _halfZoom = 5,
    _slHostCount = 0,
    _super,
    _shadowWidth = 4,
    _fontCache = {},  // { uid: { font: fontString } }
    _unitCache = {},  // { uid: { pt, em } }
    _colorCache = {}, // { color: ["#ffffff", alpha] }
    _parseColor = function(c) {
      return _colorCache[c] = uuColor.parse(c); // add cache
    },
    // property alias
    CANVAS_RENDERING_CONTEXT_2D = "CanvasRenderingContext2D",
    GLOBAL_ALPHA    = "globalAlpha",
    GLOBAL_COMPO    = "globalCompositeOperation",
    STROKE_STYLE    = "strokeStyle",
    FILL_STYLE      = "fillStyle",
    LINE_WIDTH      = "lineWidth",
    LINE_CAP        = "lineCap",
    LINE_JOIN       = "lineJoin",
    MITER_LIMIT     = "miterLimit",
    SHADOW_OFFSET_X = "shadowOffsetX",
    SHADOW_OFFSET_Y = "shadowOffsetY",
    SHADOW_BLUR     = "shadowBlur",
    SHADOW_COLOR    = "shadowColor",
    SHADOWS         = [SHADOW_COLOR, SHADOW_OFFSET_X, SHADOW_OFFSET_Y,
                       SHADOW_BLUR],
    FONT            = "font",
    TEXT_ALIGN      = "textAlign",
    TEXT_BASELINE   = "textBaseline",
    MEASURE_STYLE   = "position:absolute;border:0 none;margin:0;padding:0;",
    TRANSPARENT     = "transparent",
    // property sets
    HIT_PROPS       = { width: 1, height: 1 },
    HIT_PROPS2      = { width: 1, height: 1,
                        display: 2, visibility: 2, opacity: 2 },
    COMPOSITES      = { "source-over": 0, "destination-over": 4, copy: 10 },
    SAVE_PROPS      = { strokeStyle: 1, fillStyle: 1, globalAlpha: 1,
                        lineWidth: 1, lineCap: 1, lineJoin: 1, miterLimit: 1,
                        shadowOffsetX: 1, shadowOffsetY: 1, shadowBlur: 1,
                        shadowColor: 1, globalCompositeOperation: 1, font: 1,
                        textAlign: 1, textBaseline: 1, _lineScale: 1,
                        _scaleX: 1, _scaleY: 1, _efx: 1, _clipPath: 1 },
    CAPS            = { square: "square", butt: "flat", round: "round" },
    FONT_SIZES      = { "xx-small": 0.512, "x-small": 0.64, smaller: 0.8,
                        small: 0.8, medium: 1, large: 1.2, larger: 1.2,
                        "x-large": 1.44, "xx-large": 1.728 },
    FONT_STYLES     = { normal: "Normal", italic: "Italic", oblique: "Italic" },
    FONT_WEIGHTS    = { normal: "Normal", bold: "Bold", bolder: "ExtraBold",
                        lighter: "Thin", "100": "Thin", "200": "ExtraLight",
                        "300": "Light", "400": "Normal", "500": "Medium",
                        "600": "SemiBold", "700": "Bold", "800": "ExtraBold",
                        "900": "Black" },
    FONT_SCALES     = { ARIAL: 1.55, "ARIAL BLACK": 1.07,
                        "COMIC SANS MS": 1.15, "COURIER NEW": 1.6,
                        GEORGIA: 1.6, "LUCIDA GRANDE": 1,
                        "LUCIDA SANS UNICODE": 1, "TIMES NEW ROMAN": 1.65,
                        "TREBUCHET MS": 1.55, VERDANA: 1.4, "MS UI GOTHIC": 2,
                        "MS PGOTHIC": 2, MEIRYO: 1,
                        "SANS-SERIF": 1, SERIF: 1, MONOSPACE: 1,
                        FANTASY: 1, CURSIVE: 1 },
    FUNCS           = { 1: "_lfill", 2: "_rfill", 3: "_pfill" },
    // fragments
    SL_FILL         = '" Fill="',
    SL_STROKE       = '" Stroke="',
    SL_DATA         = '" Data="',
    SL_PATH_OPACITY = '<Path Opacity="',
    SL_CANVAS_ZINDEX= '<Canvas Canvas.ZIndex="',
    SL_CANVAS_LEFT  = '" Canvas.Left="',
    SL_CANVAS_TOP   = '" Canvas.Top="',
    VML_COORD       = '" coordsize="100,100',
    VML_FILL        = '" filled="t" stroked="f',
    VML_STROKE      = '" filled="f" stroked="t',
    VML_PATH        = '" path="',
    VML_COLOR       = '" color="',
    VML_COLORS      = '" colors="',
    VML_OPACITY     = '" opacity="',
    VML_ANGLE       = '" angle="',
    VML_FILLTYPE_HEAD = ' filltype="',
    VML_TYPE_HEAD   = ' type="',
    VML_COLOR_HEAD  = ' color="',
    VML_BASE_STYLE  = ' style="position:absolute;z-index:',
    VML_SHAPE_STYLE =
          '<v:shape style="position:absolute;width:10px;height:10px;z-index:',
    VML_END_SHAPE   = '" /></v:shape>',
    VML_VSTROKE     = '"><v:stroke',
    VML_VFILL       = '"><v:fill',
    DX_PFX          = 'progid:DXImageTransform.Microsoft';

_canvas = {
  // uuCanvas.init - initialize a canvas made dynamically
  init: function(canvas, // @param Node: canvas element
                 vml) {  // @param Boolean(= false): true = force VML
    return canvas.getContext ? canvas // already initialized
                             : (vml || !_slver) ? VMLInit(canvas)
                                                : SLInit(canvas);
  },

  // uuCanvas.ready
  ready: function(callback) { // @param Function:
    var lp = function() {
      (_ie ? _canvas.already()
           : _canvasReady) ? callback() : setTimeout(lp, 64);
    }
    setTimeout(lp, 16);
  },

  // uuCanvas.already
  already: function() { // @return Boolean: true is already
    if (!_ie) { return !!_canvasReady; }
    var node = _doc.getElementsByTagName("canvas"), i = node.length;
    while (i--) {
      if (!("uuCanvasType" in node[i])) {
        return false;
      }
    }
    return true;
  },

  // uuCanvas.expire - expire cache
  expire: function() {
    _fontCache = {};
    _unitCache = {};
    _colorCache = {};
  }
};

// 2D Matrix
_matrix = {
  multiply: function(a, b) {
    return [a[0] * b[0] + a[1] * b[3] + a[2] * b[6],  // m11
            a[0] * b[1] + a[1] * b[4] + a[2] * b[7],  // m12
            0,                                        // m13
            a[3] * b[0] + a[4] * b[3] + a[5] * b[6],  // m21
            a[3] * b[1] + a[4] * b[4] + a[5] * b[7],  // m22
            0,                                        // m23
            a[6] * b[0] + a[7] * b[3] + a[8] * b[6],  // m31(dx)
            a[6] * b[1] + a[7] * b[4] + a[8] * b[7],  // m32(dy)
            a[6] * b[2] + a[7] * b[5] + a[8] * b[8]]; // m33
  },

  translate: function(x, y) {
    return [1, 0, 0,  0, 1, 0,  x, y, 1];
  },

  rotate: function(angle) {
    var c = _cos(angle), s = _sin(angle);
    return [c, s, 0,  -s, c, 0,  0, 0, 1];
  },

  scale: function(x, y) {
    return [x, 0, 0,  0, y, 0,  0, 0, 1];
  },

  transform: function(m11, m12, m21, m22, dx, dy) {
    return [m11, m12, 0,  m21, m22, 0,  dx, dy, 1];
  }
};

function detectDrawImageArg(image) {
  var a = arguments, az = a.length,
      dim = _image.getActualDimension(image);

  if (az < 9) {
    return {
      az: az, dim: dim,
      sx: 0, sy: 0, sw: dim.w, sh: dim.h,
      dx: a[1], dy: a[2], dw: a[3] || dim.w, dh: a[4] || dim.h
    };
  } else if (az === 9) {
    return {
      az: az, dim: dim,
      sx: a[1], sy: a[2], sw: a[3], sh: a[4],
      dx: a[5], dy: a[6], dw: a[7], dh: a[8]
    };
  }
  throw "";
}

function toHTMLEntity(str) {
  return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
}

// measure text rect(width, height)
function getTextMetric(text, font) {
  if (!_metric) {
    _metric = _doc.createElement("div");
    // "left:-10000px" is fixed word wrap
    _metric.style.cssText =
        MEASURE_STYLE +
        "top:-10000px;left:-10000px;text-align:left;visibility:hidden";
    _doc.body.appendChild(_metric);
  }
  _metric.style.font = font;
  _ie ? (_metric.innerText = text)
      : (_metric.textContent = text);

  var w = 0, h = 0, rect;
  if (_metric.getBoundingClientRect) {
    rect = _metric.getBoundingClientRect();
    w = rect.right - rect.left, h = rect.bottom - rect.top;
  }
  return { w: _metric.clientWidth || w, h: _metric.clientHeight || h };
}

// parse CSS::font style
function parseFont(font, embase) {
  var rv = {}, w, sz, dummy, style, uid, key = "uuCanvasID";

  function measureUnit(elm) {
    var node = elm.appendChild(_doc.createElement("div")), pt, em;
    node.style.cssText = MEASURE_STYLE + "width:12pt;height:12em";
    pt = node.clientWidth  / 12;
    em = node.clientHeight / 12;
    elm.removeChild(node);
    return { pt: pt, em: em };
  }

  uid = embase[key] || (embase[key] = ++_uid);

  if (uid in _fontCache) {
    if (font in _fontCache[uid]) {
      return _fontCache[uid][font];
    }
  } else {
    _fontCache[uid] = {};
  }

  // computed font style by CSS parser
  dummy = _doc.createElement("div");
  style = dummy.style;
  try {
    style.font = font;
  } catch (err) {}

  sz = style.fontSize;

  if ( (w = FONT_SIZES[sz]) ) {
    w *= 16;
  } else {
    w = _float(sz);
    if (/pt$/.test(sz)) { // "12.3pt"
      w *= 1.33; // 1.3333...
    } else if (/em$/.test(sz)) { // "10.5em"
      if (!(uid in _unitCache)) {
        _unitCache[uid] = measureUnit(embase);
      }
      w *= _unitCache[uid].em;
    }
  }
  rv.size = _float(w);
  rv.style = style.fontStyle;
  rv.weight = style.fontWeight;
  rv.variant = style.fontVariant;
  rv.rawfamily = style.fontFamily.replace(/[\"\']/g, "");
  rv.family = "'" + rv.rawfamily.replace(/\s*,\s*/g, "','") + "'";
  rv.formal = [rv.style, rv.variant, rv.weight, rv.size.toFixed(2) + "px",
               rv.family].join(" ");
  return _fontCache[uid][font] = rv;
}

function applyCanvasSize(elm) {
  var e = elm, attr = e.attributes;
  if (attr.width && attr.width.specified) {
    e.style.pixelWidth = _int(attr.width.nodeValue);
  } else {
    e.width = e.clientWidth;
  }
  if (attr.height && attr.height.specified) {
    e.style.pixelHeight = _int(attr.height.nodeValue);
  } else {
    e.height = e.clientHeight;
  }
}

function strokeProps(obj, vml) {
  var cap = CAPS[obj[LINE_CAP]],
      join = obj[LINE_JOIN],
      width = (obj[LINE_WIDTH] * obj._lineScale).toFixed(2),
      miter = obj[MITER_LIMIT];
  if (!vml) {
    return ['" StrokeLineJoin="', join,
            '" StrokeMiterLimit="', miter,
            '" StrokeThickness="', width,
            '" StrokeStartLineCap="', cap,
            '" StrokeEndLineCap="', cap].join("");
  }
  return ['" weight="', width, 'px" endcap="', cap,
          '" joinstyle="', join,
          '" miterlimit="', miter].join("");
}

_super = {
  save: function() {
    var prop = {}, i;
    for (i in SAVE_PROPS) {
      prop[i] = this[i];
    }
    this._stack.push([prop, _mix([], this._mtx),
                      this._clipPath ? String(this._clipPath) : null]);
  },

  restore: function() {
    if (!this._stack.length) { return; }

    var last = this._stack.pop(), i;
    for (i in SAVE_PROPS) {
      this[i] = last[0][i];
    }
    this._mtx = last[1];
    this._clipPath = last[2];
  },

  scale: function(x, y) {
    this._efx = 1;
    // inlining
    this._mtx = _matrix.multiply([x, 0, 0,  0, y, 0,  0, 0, 1], this._mtx);
    this._scaleX *= x;
    this._scaleY *= y;
    this._lineScale = (this._mtx[0] + this._mtx[4]) / 2;
  },

  rotate: function(angle) {
    this._efx = 1;
    var c = _cos(angle), s = _sin(angle);
    // inlining
    this._mtx = _matrix.multiply([c, s, 0,  -s, c, 0,  0, 0, 1], this._mtx);
  },

  translate: function(x, y) {
    this._efx = 1;
    // inlining
    this._mtx = _matrix.multiply([1, 0, 0,  0, 1, 0,  x, y, 1], this._mtx);
  },

  transform: function(m11, m12, m21, m22, dx, dy) {
    this._efx = 1;
    // inlining
    this._mtx = _matrix.multiply([m11, m12, 0,  m21, m22, 0,  dx, dy, 1],
                                 this._mtx);
  },

  setTransform: function(m11, m12, m21, m22, dx, dy) {
    // reset _efx flag
    this._efx = (m11 === 1 && !m12 &&
                 !m21 && m22 === 1 && !dx && !dy) ? 0 : 1;
    this._mtx = _matrix.transform(m11, m12, m21, m22, dx, dy);
  },

  strokeRect: function(x, y, w, h) {
    this.fill(1, this._rect(x, y, w, h));
  },

  beginPath: function() {
    this._path = [];
  },

  arcTo: function(x1, y1, x2, y2, radius) {
    // not impl
  },

  stroke: function() {
    this.fill(1);
  },

  isPointInPath: function(x, y) {
    // not impl
  },

  strokeText: function(text, x, y, maxWidth) {
    this.fillText(text, x, y, maxWidth, 1);
  },

  measureText: function(text) {
    var metric = getTextMetric(text, this[FONT]);
    return new TextMetrics(metric.w, metric.h);
  },

  createImageData: function(sw, sh) {
    // not impl
  },

  getImageData: function(sx, sy, sw, sh) {
    // not impl
  },

  putImageData: function(imagedata, dx, dy, dirtyX, dirtyY,
                         dirtyWidth, dirtyHeight) {
    // not impl
  },

  _initSurface: function(resize) {
    _mix(this, {
      // --- compositing ---
      globalAlpha:    1.0,
      globalCompositeOperation: "source-over",
      // --- colors and styles ---
      strokeStyle:    "#000000", // black
      fillStyle:      "#000000", // black
      // --- line caps/joins ---
      lineWidth:      1,
      lineCap:        "butt",
      lineJoin:       "miter",
      miterLimit:     10,
      // --- shadows ---
      shadowOffsetX:  0,
      shadowOffsetY:  0,
      shadowBlur:     0,
      shadowColor:    TRANSPARENT, // transparent black
      // --- text ---
      font:           "10px sans-serif",
      textAlign:      "start",
      textBaseline:   "alphabetic",
      // --- extend properties ---
      xMissColor:     "#000000", // black
      xTextMarginTop: 1.3, // for VML
      xClipStyle:     0, // for VML
      xImageRender:   0, // 0: normal, 1: vml:image
      xFlyweight:     0, // for Silverlight, VML
      xShadowOpacityFrom:  0.01, // for Silverlight, VML
      xShadowOpacityDelta: 0.05, // for Silverlight, VML
      // --- hidden properties ---
      _lineScale:     1,
      _scaleX:        1,
      _scaleY:        1,
      _zindex:        -1,
      _efx:           0 // 1: matrix effected
    });

    this._mtx = [1, 0, 0,  0, 1, 0,  0, 0, 1]; // Matrix.identity
    this._history = []; // canvas rendering history
    this._stack = []; // matrix and prop stack.
    this._path = []; // current path
    this._clipPath = null; // clipping path

    if (this.canvas.uuCanvasType === "VML2D") {
      this._shadow = ["#000", 0, 0, 0];
      this._px = 0; // current position x
      this._py = 0; // current position y
      if (resize) {
        this._elm.style.pixelWidth = this.canvas.width;
        this._elm.style.pixelHeight = this.canvas.height;
      }
    } else {
      this._shadow = ["#000", 0, 0, 0];
      this.xShadowBlur = _slver >= 3 ? 1 : 0;
      this.xTiling = 1; // 1 = TileBrush simulate(slow)
      this._clipRect = null; // clipping rect
    }
    return this;
  }
};

function onPropertyChange(evt) {
  var tgt, name = evt.propertyName;
  if (HIT_PROPS[name]) {
    tgt = evt.srcElement;
    tgt.style[name] = _max(_int(tgt.attributes[name].nodeValue), 0) + "px";
    tgt.uuCanvasType && tgt.getContext()._initSurface(1)._clear();
  }
}

function TextMetrics(w, h) { // for measureText
  this.width = w;
  this.height = h;
}

function Patt(image, repetition) { // for createPattern
  repetition = repetition || "repeat";
  switch (repetition) {
  case "repeat": break;
  default: throw "";
  }

  if (!("src" in image)) { // HTMLCanvasElement unsupported
    throw "";
  }
  this._src = image.src; // HTMLImageElement
  this._dim = _image.getActualDimension(image);
  this._type = 3; // 3:tile
  this._repeat = repetition;
}

function Grad(type, param, vml) { // for create(Linear|Radial)Gradient
  this._vml = vml;
  this._type = type;
  this._param = param;
  this._colorStop = [];
}

Grad.prototype.addColorStop = function(offset, color) {
  function fn(a, b) {
    return a.offset - b.offset;
  }

  var c = _colorCache[color] || _parseColor(color),
      v, i = 0, iz;

  if (!this._vml) { // SL
    this._colorStop.push({ offset: offset, color: c });
  } else { // VML
    // collision of the offset is evaded
    for (iz = this._colorStop.length; i < iz; ++i) {
      v = this._colorStop[i];
      if (v.offset === offset) {
        if (offset < 1 && offset > 0) {
          offset += iz / 1000; // collision -> +0.001
        }
      }
    }
    this._colorStop.push({ offset: 1 - offset, color: c });
  }
  this._colorStop.sort(fn); // sort offset
};

function removeFallbackContents(elm) {
  if (!elm.parentNode) {
    return elm;
  }
  var rv = _doc.createElement(elm.outerHTML),
      endTags = _doc.getElementsByTagName("/CANVAS"),
      idx = elm.sourceIndex,
      v, w, i = 0, iz = endTags.length;
  for (; i < iz; ++i) {
    if (idx < endTags[i].sourceIndex &&
        elm.parentNode === endTags[i].parentNode) {
      v = _doc.all[endTags[i].sourceIndex];
      do {
        w = v.previousSibling; // keep previous
        v.parentNode.removeChild(v);
        v = w;
      } while (v !== elm);
      break;
    }
  }
  elm.parentNode.replaceChild(rv, elm);
  return rv;
}

// --- Silverlight ---
function SLInit(elm) {
  var e = removeFallbackContents(elm),
      onload = "_sl" + (++_slHostCount) + "_onload";

  applyCanvasSize(e);
  e.getContext = function() { return e._ctx2d; };
  e._ctx2d = new SL2D(e);
  _win[onload] = function(sender) {
    e.uuCanvasType = "SL2D"; // canvas.already mark
    // lazy detection
    e.style.direction = e.currentStyle.direction;
    // sender is <Canvas> element
    // sender.getHost() is <object> element
    e._ctx2d._view = sender.children;
    e._ctx2d._content = sender.getHost().content;
    _win[onload] = void 0; // free event-hander
  };
  e.innerHTML = [
    '<object type="application/x-silverlight" width="100%" height="100%">',
      '<param name="background" value="#00000000" />',  // transparent
      '<param name="windowless" value="true" />',
      '<param name="source" value="#xaml" />',          // XAML ID
      '<param name="onLoad" value="', onload, '" />',   // bond to global
    '</object>'].join("");
  e.bind = function() {
    e.attachEvent("onpropertychange", onPropertyChange);
  };
  e.unbind = function() {
    e.detachEvent("onpropertychange", onPropertyChange);
  };
  e.bind();
  return e;
}

// Silverlight 2D
function SL2D(elm) {
  this.canvas = elm;
  this._initSurface();
  this._elm = elm;
  this._view = null;
  this._content = null;
};

_mix(SL2D.prototype, _super, {
  _rect: function(x, y, w, h) {
    if (this._efx) {
      var c0 = this._map(x, y),
          c1 = this._map(x + w, y),
          c2 = this._map(x + w, y + h),
          c3 = this._map(x, y + h);
      return [" M", c0.x, " ", c0.y,
              " L", c1.x, " ", c1.y,
              " L", c2.x, " ", c2.y,
              " L", c3.x, " ", c3.y,
              " Z"].join("");
    }
    return [" M", x,     " ", y,
            " L", x + w, " ", y,
            " L", x + w, " ", y + h,
            " L", x,     " ", y + h,
            " Z"].join("");
  },

  _map: function(x, y) {
    var m = this._mtx;
    return {
      x: x * m[0] + y * m[3] + m[6], // x * m11 + y * m21 + dx
      y: x * m[1] + y * m[4] + m[7]  // x * m12 + y * m22 + dy
    }
  },

  // === State =============================================
  // === Transformations ===================================
  // === Rects =============================================
  clearRect: function(x, y, w, h) {
    w = _int(w), h = _int(h);
    if ((!x && !y &&
         w == this.canvas.width &&
         h == this.canvas.height)) {
      this._clear(); // clear all
    } else {
      var zindex = 0, c = _style.getBGColor(this._elm, 1), xaml;

      switch (COMPOSITES[this[GLOBAL_COMPO]]) {
      case  4: zindex = --this._zindex; break;
      case 10: this._clear();
      }

      xaml = [SL_PATH_OPACITY, c[1] * this[GLOBAL_ALPHA],
              '" Canvas.ZIndex="', zindex,
              SL_FILL, c[0],
              SL_DATA, this._rect(x, y, w, h), '" />'].join("");
      !this.xFlyweight &&
        this._history.push(this._clipPath ? (xaml = this._clippy(xaml)) : xaml);
      this._view.add(this._content.createFromXaml(xaml, false));
    }
  },

  _clear: function(x, y, w, h) {
    this._history = [];
    this._zindex = 0;
    this._view && this._view.clear(); // fix for IE8
  },

  fillRect: function(x, y, w, h) {
    this.fill(0, this._rect(x, y, w, h));
  },

  // === Path API ==========================================
  closePath: function() {
    this._path.push(" Z");
  },

  moveTo: function(x, y) {
    if (this._efx) {
      var m = this._mtx; // inlining: this._map(x, y)
      this._path.push(" M", x * m[0] + y * m[3] + m[6], " ",
                            x * m[1] + y * m[4] + m[7]);
    } else {
      this._path.push(" M", x, " ", y);
    }
  },

  lineTo: function(x, y) {
    if (this._efx) {
      var m = this._mtx; // inlining: this._map(x, y)
      this._path.push(" L", x * m[0] + y * m[3] + m[6], " ",
                            x * m[1] + y * m[4] + m[7]);
    } else {
      this._path.push(" L", x, " ", y);
    }
  },

  quadraticCurveTo: function(cpx, cpy, x, y) {
    if (this._efx) {
      var c0 = this._map(cpx, cpy), c1 = this._map(x, y);
      cpx = c0.x, cpy = c0.y, x = c1.x, y = c1.y;
    }
    this._path.push(" Q", cpx, " ", cpy, " ", x, " ", y);
  },

  bezierCurveTo: function(cp1x, cp1y, cp2x, cp2y, x, y) {
    if (this._efx) {
      var c0 = this._map(cp1x, cp1y), c1 = this._map(cp2x, cp2y),
          c2 = this._map(x, y);
      cp1x = c0.x, cp1y = c0.y, cp2x = c1.x, cp2y = c1.y, x = c2.x, y = c2.y;
    }
    this._path.push(" C", cp1x, " ", cp1y, " ", cp2x, " ", cp2y, " ",
                          x, " ", y);
  },

  rect: function(x, y, w, h) {
    this._path.push(this._rect(x, y, w, h));
  },

  arc: function(x, y, radius, startAngle, endAngle, anticlockwise) {
    var deg1 = startAngle * _deg,
        deg2 = endAngle * _deg,
        isLargeArc = 0, magic = 0.0001570796326795,
        sweepDirection = anticlockwise ? 0 : 1,
        sx, sy, ex, ey, rx, ry, c0;

    // angle normalize
    if (deg1 < 0)   { deg1 += 360; }
    if (deg1 > 360) { deg1 -= 360; }
    if (deg2 < 0)   { deg2 += 360; }
    if (deg2 > 360) { deg2 -= 360; }

    // circle
    if (deg1 + 360 == deg2 || deg1 == deg2 + 360) {
      if (sweepDirection) {
        endAngle -= magic;
      } else {
        endAngle += magic;
      }
      isLargeArc = 1;
    } else if (sweepDirection) {
      if (deg2 - deg1 > 180) {
        isLargeArc = 1;
      }
    } else {
      if (deg1 - deg2 > 180) {
        isLargeArc = 1;
      }
    }

    rx = this._scaleX * radius;
    ry = this._scaleY * radius;

    sx = x + (_cos(startAngle) * radius);
    sy = y + (_sin(startAngle) * radius);
    ex = x + (_cos(endAngle) * radius);
    ey = y + (_sin(endAngle) * radius);

    // add <PathFigure StartPoint="..">
    this._path.length ? this.lineTo(sx, sy)
                      : this.moveTo(sx, sy);
    if (this._efx) {
      c0 = this._map(ex, ey);
      ex = c0.x;
      ey = c0.y;
    }
    this._path.push(" A", rx, " ", ry, " 0 ", isLargeArc, " ",
                    sweepDirection, " ", ex, " ", ey);
  },

  fill: function(wire, path) {
    path = path || this._path.join("");

    var rv = [], xaml, zindex = 0, mix, c,
        style = wire ? this[STROKE_STYLE] : this[FILL_STYLE],
        // for shadow
        si = 0, so = 0, sd = 0, sx = 0, sy = 0,
        sc = _colorCache[this[SHADOW_COLOR]] ||
             _parseColor(this[SHADOW_COLOR]);

    if ( (mix = COMPOSITES[this[GLOBAL_COMPO]]) ) {
      (mix === 4) ? (zindex = --this._zindex) : this._clear();
    }

    if (typeof style === "string") {
      c = _colorCache[style] || _parseColor(style);

      rv.push(SL_CANVAS_ZINDEX, zindex, '">');

      if (sc[1] && !this.xShadowBlur) {
        sx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
        sy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
        so = this.xShadowOpacityFrom;
        sd = this.xShadowOpacityDelta;

        for (; si < _shadowWidth; so += sd, --sx, --sy, ++si) {
          rv.push(SL_PATH_OPACITY, so.toFixed(2),
                  SL_CANVAS_LEFT, sx, SL_CANVAS_TOP, sy,
                  SL_DATA, path,
                  wire ? strokeProps(this) : "",
                  wire ? SL_STROKE : SL_FILL, sc[0], '" />');
        }
      }
      rv.push(SL_PATH_OPACITY, c[1] * this[GLOBAL_ALPHA],
              SL_DATA, path,
              wire ? strokeProps(this) : "",
              wire ? SL_STROKE : SL_FILL, c[0], '">',
              (sc[1] && this.xShadowBlur) ? this._blur("Path", sc) : "",
              '</Path></Canvas>');
      xaml = rv.join("");
    } else {
      xaml = this[FUNCS[style._type]](style, path, wire, mix, zindex, sc);
    }
    !this.xFlyweight &&
      this._history.push(this._clipPath ? (xaml = this._clippy(xaml)) : xaml);
    this._view.add(this._content.createFromXaml(xaml, false));
  },

  // LinearGradient fill
  _lfill: function(style, path, wire, mix, zindex, shadowColor) {
    var rv = [],
        fp = style._param,
        color = this._lcolor(style._colorStop),
        prop = wire ? "Stroke" : "Fill",
        c0 = this._map(fp.x0, fp.y0), c1 = this._map(fp.x1, fp.y1),
        // for shadow
        si = 0, siz = _shadowWidth, sc, so = 0, sd = 0, shx = 0, shy = 0;

    rv.push(SL_CANVAS_ZINDEX, zindex, '">');

    if (shadowColor[1] && !this.xShadowBlur) {
      sc = this._lcolor([
        { offset: 0.0, color: shadowColor },
        { offset: 1.0, color: shadowColor }
      ]);
      shx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
      shy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
      so = this.xShadowOpacityFrom;
      sd = this.xShadowOpacityDelta;

      if (wire) {
        siz = this[LINE_WIDTH];
        sd = 0.2 / siz; // opacity from 0.05 to 0.25
      }
      for (; si < siz; so += sd, --shx, --shy, ++si) {
        rv.push(SL_PATH_OPACITY, so.toFixed(2),
                SL_CANVAS_LEFT, shx, SL_CANVAS_TOP, shy,
                SL_DATA, path,
                wire ? strokeProps(this) : "", '"><Path.', prop,
                '><LinearGradientBrush MappingMode="Absolute" StartPoint="',
                c0.x, ",", c0.y,
                '" EndPoint="', c1.x, ",", c1.y, '">', sc,
                '</LinearGradientBrush></Path.', prop, '></Path>');
      }
    }

    rv.push(SL_PATH_OPACITY, this[GLOBAL_ALPHA],
            SL_DATA, path,
            wire ? strokeProps(this) : "", '"><Path.', prop,
            '><LinearGradientBrush MappingMode="Absolute" StartPoint="',
            c0.x, ",", c0.y,
              '" EndPoint="', c1.x, ",", c1.y, '">', color,
            '</LinearGradientBrush></Path.', prop, '>',
              (shadowColor[1] &&
               this.xShadowBlur) ? this._blur("Path", shadowColor) : "",
            '</Path></Canvas>');
    return rv.join("");
  },

  // RadialGradient fill
  _rfill: function(style, path, wire, mix, zindex, shadowColor) {
    var rv = [], prop = wire ? "Stroke" : "Fill",
        fp = style._param,
        zindex2 = 0,
        color = this._rcolor(style),
        rr = fp.r1 * 2,
        x = fp.x1 - fp.r1,
        y = fp.y1 - fp.r1,
        gx = (fp.x0 - (fp.x1 - fp.r1)) / rr,
        gy = (fp.y0 - (fp.y1 - fp.r1)) / rr,
        m = _matrix.multiply(_matrix.translate(x, y), this._mtx),
        tmpmtx = this._trns('Ellipse', m),
        v, bari = "",
        // for shadow
        si = 0, siz = _shadowWidth, so = 0, sd = 0, sx = 0, sy = 0;

    rv.push(SL_CANVAS_ZINDEX, zindex, '">');

    if (shadowColor[1] && !this.xShadowBlur) {
      sx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
      sy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
      so = this.xShadowOpacityFrom;
      sd = this.xShadowOpacityDelta;

      if (wire) {
        siz = this[LINE_WIDTH];
        sd = 0.2 / siz; // opacity from 0.05 to 0.25
      }

      for (; si < siz; so += sd, --sx, --sy, ++si) {
        rv.push('<Ellipse Opacity="', so.toFixed(2),
                SL_CANVAS_LEFT, sx, SL_CANVAS_TOP, sy,
                '" Width="', rr, '" Height="', rr,
                wire ? strokeProps(this) : "",
                wire ? SL_STROKE : SL_FILL, shadowColor[0],
                '">', tmpmtx, '</Ellipse>');
      }
    }

    if (!wire) {
      // fill outside
      if (style._colorStop.length) {
        v = style._colorStop[style._colorStop.length - 1];
        if (v.color[1] > 0.001) {
          if (mix === 4) { zindex2 = --this._zindex; }
          bari =  [ SL_PATH_OPACITY, this[GLOBAL_ALPHA],
                    '" Canvas.ZIndex="', zindex2,
                    SL_DATA, path, SL_FILL, '#',
                    _hex[_float(v.color[1] / (1 / 255))] +
                    v.color[0].substring(1),
                    '" />'].join("");
          !this.xFlyweight &&
            this._history.push(this._clipPath ? (bari = this._clippy(bari))
                                              : bari);
          this._view.add(this._content.createFromXaml(bari, false));
        }
      }
    }

    rv.push('<Ellipse Opacity="', this[GLOBAL_ALPHA],
            '" Width="', rr, '" Height="', rr,
            wire ? strokeProps(this) : "",
            '"><Ellipse.', prop, '><RadialGradientBrush GradientOrigin="',
            gx, ',', gy,
            '" Center="0.5,0.5" RadiusX="0.5" RadiusY="0.5">', color,
            '</RadialGradientBrush></Ellipse.', prop, '>',
              tmpmtx,
              (shadowColor[1] &&
               this.xShadowBlur) ? this._blur("Ellipse", shadowColor) : "",
            '</Ellipse></Canvas>');
    return rv.join("");
  },

  // Pattern fill
  _pfill: function(style, path, wire, mix, zindex, shadowColor) {
    var rv = [], prop = wire ? "Stroke" : "Fill",
        zindex2 = 0,
        sw, sh, xz, yz, x, y, // use tile mode
        // for shadow
        si = 0, so = 0, sd = 0, sx = 0, sy = 0;

    if (!wire && this.xTiling) {
      x  = 0;
      y  = 0;
      sw = style._dim.w;
      sh = style._dim.h;
      xz = _ceil(_int(this.canvas.width)  / sw);
      yz = _ceil(_int(this.canvas.height) / sh);

      if (mix === 4) { zindex2 = --this._zindex; }

      rv.push(SL_CANVAS_ZINDEX, zindex, '">');

      if (shadowColor[1]) {
        sx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
        sy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
        so = this.xShadowOpacityFrom;
        sd = this.xShadowOpacityDelta;

        for (; si < _shadowWidth; so += sd, --sx, --sy, ++si) {
          rv.push(SL_PATH_OPACITY, so.toFixed(2),
                  SL_CANVAS_LEFT, sx, SL_CANVAS_TOP, sy,
                  SL_DATA, path, wire ? strokeProps(this) : "",
                  SL_FILL, shadowColor[0],
                  '" />');
        }
      }

      rv.push(SL_CANVAS_ZINDEX, zindex2, '" Clip="', path, '">');
      for (y = 0; y < yz; ++y) {
        for (x = 0; x < xz; ++x) {
          rv.push('<Image Opacity="', this[GLOBAL_ALPHA],
                  SL_CANVAS_LEFT, x * sw, SL_CANVAS_TOP, y * sh,
                  '" Source="', style._src, '">',
//                  (shadowColor[1] &&
//                   this.xShadowBlur) ? this._blur("Image", shadowColor) : "",
                  '</Image>');
        }
      }
      rv.push('</Canvas></Canvas>');

      return rv.join("");
    }

    rv.push(SL_CANVAS_ZINDEX, zindex, '">');

    if (shadowColor[1] && !this.xShadowBlur) {
      sx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
      sy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
      so = this.xShadowOpacityFrom;
      sd = this.xShadowOpacityDelta;

      for (; si < _shadowWidth; so += sd, --sx, --sy, ++si) {
        rv.push(SL_PATH_OPACITY, so.toFixed(2),
                SL_CANVAS_LEFT, sx, SL_CANVAS_TOP, sy,
                SL_DATA, path, wire ? strokeProps(this) : "",
                '"><Path.', prop, '><ImageBrush Stretch="None" ImageSource="',
                style._src,
                '" /></Path.', prop, '></Path>');
      }
    }

    rv.push(SL_PATH_OPACITY, this[GLOBAL_ALPHA],
            wire ? strokeProps(this) : "",
            SL_DATA, path,
            '"><Path.', prop, '><ImageBrush Stretch="None" ImageSource="',
            style._src,
            '" /></Path.', prop, '>',
              (shadowColor[1] &&
               this.xShadowBlur) ? this._blur("Path", shadowColor) : "",
            '</Path></Canvas>');
    return rv.join("");
  },

  clip: function() {
    this._clipPath = this._path.join("");
  },

  _clippy: function(xaml) {
    return ['<Canvas Clip="', this._clipPath, '">', xaml,
            '</Canvas>'].join("");
  },

  // === Text ==============================================
  fillText: function(text, x, y, maxWidth, wire) {
    text = text.replace(/(\t|\v|\f|\r\n|\r|\n)/g, " ");
    var style = wire ? this[STROKE_STYLE] : this[FILL_STYLE],
        types = (typeof style === "string") ? 0 : style._type,
        rv = [], xaml, c, fp, c0, c1, zindex = 0, mtx, rgx, rgy,
        font = parseFont(this[FONT], this.canvas),
        metric = getTextMetric(text, font.formal),
        offX = 0, align = this[TEXT_ALIGN], dir = "ltr",
        // for shadow
        si = 0, so = 0, sd = 0, sx = 0, sy = 0,
        sc = _colorCache[this[SHADOW_COLOR]] ||
             _parseColor(this[SHADOW_COLOR]);

    switch (align) {
    case "end": dir = "rtl"; // break;
    case "start":
      align = this._elm.style.direction === dir ? "left" : "right"
    }
    if (align === "center") {
      offX = (metric.w - 4) / 2; // -4: adjust
    } else if (align === "right") {
      offX = metric.w;
    }

    mtx = this._trns('TextBlock',
                     _matrix.multiply(_matrix.translate(x - offX, y),
                                      this._mtx));

    switch (COMPOSITES[this[GLOBAL_COMPO]]) {
    case  4: zindex = --this._zindex; break;
    case 10: this._clear();
    }

    rv.push(SL_CANVAS_ZINDEX, zindex, '">');

    if (sc[1] && !this.xShadowBlur) {
      sx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
      sy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
      so = _max(this.xShadowOpacityFrom + 0.9, 1);
      sd = this.xShadowOpacityDelta;

      for (; si < _shadowWidth; so += sd, --sx, --sy, ++si) {
        rv.push('<TextBlock Opacity="', so.toFixed(2),
                '" Foreground="', sc[0],
                SL_CANVAS_LEFT, sx, SL_CANVAS_TOP, sy,
                '" FontFamily="', font.rawfamily,
                '" FontSize="', font.size.toFixed(2),
                '" FontStyle="', FONT_STYLES[font.style] || "Normal",
                '" FontWeight="', FONT_WEIGHTS[font.weight] || "Normal",
                '">', toHTMLEntity(text), mtx, '</TextBlock>');
      }
    }

    if (!types) {
      c = _colorCache[style] || _parseColor(style);
      rv.push('<TextBlock Opacity="', c[1] * this[GLOBAL_ALPHA],
              '" Foreground="', c[0]);
    } else {
      rv.push('<TextBlock Opacity="', this[GLOBAL_ALPHA]);
    }
    rv.push('" FontFamily="', font.rawfamily,
            '" FontSize="', font.size.toFixed(2),
            '" FontStyle="', FONT_STYLES[font.style] || "Normal",
            '" FontWeight="', FONT_WEIGHTS[font.weight] || "Normal",
            '">', toHTMLEntity(text), mtx,
              (sc[1] && this.xShadowBlur) ? this._blur("TextBlock", sc) : "");

    switch (types) {
    case 1: c = this._lcolor(style._colorStop);
            fp = style._param;
            c0 = this._map(fp.x0, fp.y0), c1 = this._map(fp.x1, fp.y1),
            rv.push('<TextBlock.Foreground>',
                    '<LinearGradientBrush MappingMode="Absolute" StartPoint="',
                    c0.x, ",", c0.y,
                    '" EndPoint="', c1.x, ",", c1.y, '">', c,
                    '</LinearGradientBrush></TextBlock.Foreground>');
            break;
    case 2: c = this._rcolor(style);
            fp = style._param,
            rgx = (fp.x0 - (fp.x1 - fp.r1)) / (fp.r1 * 2),
            rgy = (fp.y0 - (fp.y1 - fp.r1)) / (fp.r1 * 2),
            rv.push('<TextBlock.Foreground>',
                    '<RadialGradientBrush GradientOrigin="', rgx, ',', rgy,
                    '" Center="0.5,0.5" RadiusX="0.5" RadiusY="0.5">', c,
                    '</RadialGradientBrush></TextBlock.Foreground>');
            break;
    case 3: rv.push('<TextBlock.Foreground>',
                    '<ImageBrush Stretch="None" ImageSource="', style._src,
                    '" /></TextBlock.Foreground>');
    }
    rv.push('</TextBlock></Canvas>');
    xaml = rv.join("");
    !this.xFlyweight &&
      this._history.push(this._clipPath ? (xaml = this._clippy(xaml)) : xaml);
    this._view.add(this._content.createFromXaml(xaml, false));
  },

  // === Drawing images ====================================
  // drawImage(image, dx, dy)
  // drawImage(image, dx, dy, dw, dh)
  // drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
  drawImage: function(image) {
    var info = detectDrawImageArg.apply(this, arguments),
        dx = info.dx,
        dy = info.dy,
        dw = info.dw,
        dh = info.dh,
        sx = info.sx,
        sy = info.sy,
        sw = info.sw,
        sh = info.sh,
        iw = info.dim.w,
        ih = info.dim.h,
        rv = [], xaml,
        bw, bh, w, h, x, y, // slice
        m, tmpmtx, size = "", clip = "",
        zindex = 0, sclip = "",
        i = 0, iz, // for copy canvas
        // for shadow
        si = 0, so = 0, sd = 0, shx = 0, shy = 0,
        sc = _colorCache[this[SHADOW_COLOR]] ||
             _parseColor(this[SHADOW_COLOR]);

    switch (COMPOSITES[this[GLOBAL_COMPO]]) {
    case  4: zindex = --this._zindex; break;
    case 10: this._clear();
    }

    if ("src" in image) { // image is HTMLImageElement
      switch (info.az) {
      case 3:
        m = _matrix.multiply(_matrix.translate(dx, dy), this._mtx);
        break;
      case 5:
        m = _matrix.multiply(_matrix.translate(dx, dy), this._mtx);
        size = ['" Width="', dw, '" Height="', dh].join("");
        break;
      case 9:
        // TODO: image ratio
        //
        bw = dw / sw; // bias width
        bh = dh / sh; // bias height
        w = bw * iw;
        h = bh * ih;
        x = dx - (bw * sx);
        y = dy - (bh * sy);

        m = _matrix.multiply(_matrix.translate(x, y), this._mtx);

        size = ['" Width="', w, '" Height="', h].join("");
        clip = ['<Image.Clip><RectangleGeometry Rect="',
                  [dx - x, dy - y, dw, dh].join(" "),
                '" /></Image.Clip>'].join("");
        if (sc[1] && !this.xShadowBlur) {
          sclip = ['<Rectangle.Clip><RectangleGeometry Rect="',
                    [dx - x, dy - y, dw, dh].join(" "),
                   '" /></Rectangle.Clip>'].join("");
        }
      }

      rv.push(SL_CANVAS_ZINDEX, zindex, '">');

      if (sc[1] && !this.xShadowBlur) {
        shx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
        shy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
        so = this.xShadowOpacityFrom;
        sd = this.xShadowOpacityDelta;
        tmpmtx = this._trns('Rectangle', m);

        for (; si < _shadowWidth; so += sd, --shx, --shy, ++si) {
          rv.push('<Rectangle Opacity="', so.toFixed(2),
                  SL_CANVAS_LEFT, shx, SL_CANVAS_TOP, shy,
                  size, SL_FILL, sc[0], '">', sclip,
                  tmpmtx,
                  '</Rectangle>');
        }
      }

      rv.push('<Image Opacity="', this[GLOBAL_ALPHA],
              '" Source="', image.src, size, '">',
              clip, this._trns('Image', m),
              (sc[1] && this.xShadowBlur) ? this._blur("Image", sc) : "",
              '</Image></Canvas>');
      xaml = rv.join("");
      !this.xFlyweight &&
        this._history.push(this._clipPath ? (xaml = this._clippy(xaml)) : xaml);
      this._view.add(this._content.createFromXaml(xaml, false));
    } else { // HTMLCanvasElement
      iz = image._ctx2d._history.length;
      switch (info.az) {
      case 3:
        m = _matrix.multiply(_matrix.translate(dx, dy), this._mtx);
        break;
      case 5:
        m = _matrix.multiply(_matrix.translate(dx, dy), this._mtx);
        m = _matrix.multiply(_matrix.scale(dw / iw, dh / ih), m);
        break;
      case 9:
        bw = dw / sw; // bias width
        bh = dh / sh; // bias height
        w = bw * iw;
        h = bh * ih;
        x = dx - (bw * sx);
        y = dy - (bh * sy);

        m = _matrix.multiply(_matrix.translate(x, y), this._mtx);
        m = _matrix.multiply(_matrix.scale(bw, bh), m);

        clip = ['<Canvas.Clip><RectangleGeometry Rect="',
                  [(dx - x) / bw, (dy - y) / bh, dw / bw, dh / bh].join(" "),
                '" /></Canvas.Clip>'].join("");
//        if (sc[1] && !this.xShadowBlur) {
//          sclip = ['<Rectangle.Clip><RectangleGeometry Rect="',
//                   [(dx - x) / bw, (dy - y) / bh, dw / bw, dh / bh].join(" "),
//                   '" /></Rectangle.Clip>'].join("");
//        }
      }

      // shadow not impl

      rv.push(SL_CANVAS_ZINDEX, zindex,
              '" Opacity="', this[GLOBAL_ALPHA], // image._ctx2d[GLOBAL_ALPHA],
              size, '">',
              clip, this._trns('Canvas', m),
//              (sc[1] && this.xShadowBlur) ? this._blur("Canvas", sc) : "",
              '<Canvas>');

      for (; i < iz; ++i) {
        rv.push(image._ctx2d._history[i]);
      }
      rv.push('</Canvas></Canvas>');

      xaml = rv.join("");
      !this.xFlyweight &&
        this._history.push(this._clipPath ? (xaml = this._clippy(xaml)) : xaml);
      this._view.add(this._content.createFromXaml(xaml, false));
    }
  },

  // === Pixel manipulation ================================
  // === Gradient ==========================================
  createLinearGradient: function(x0, y0, x1, y1) {
    return new Grad(1, // 1:LinearGradient
                    { x0: x0, y0: y0, x1: x1, y1: y1 });
  },

  createRadialGradient: function(x0, y0, r0, x1, y1, r1) {
    return new Grad(2, // 2:RadialGradient
                    { x0: x0, y0: y0, r0: r0, x1: x1, y1: y1, r1: r1 });
  },

  createPattern: function(image, repetition) {
    return new Patt(image, repetition);
  },

  // build Linear Color
  _lcolor: function(ary) {
    var rv = [], v, i = 0, iz = ary.length, n = 1 / 255;
    for (; i < iz; ++i) {
      v = ary[i];
      rv.push('<GradientStop Color="#',
                _hex[_float(v.color[1] / n)],
                v.color[0].substring(1),
                '" Offset="', v.offset, '" />');
    }
    return rv.join("");
  },

  // build Radial Color
  _rcolor: function(style) {
    var rv = [],
        fp = style._param, n = 1 / 255,
        r0 = fp.r0 / fp.r1,
        remain = 1 - r0,
        v,
        i = 0,
        iz = style._colorStop.length;
    if (!iz) { return ""; }

    rv.push('<GradientStop Color="#',
              _hex[_float(style._colorStop[0].color[1] / n)],
              style._colorStop[0].color[0].substring(1),
              '" Offset="', 0, '" />');
    for (i = 0; i < iz; ++i) {
      v = style._colorStop[i];
      rv.push('<GradientStop Color="#',
                _hex[_float(v.color[1] / n)],
                v.color[0].substring(1),
                '" Offset="', (v.offset * remain + r0), '" />');
    }
    return rv.join("");
  },

  // build MatrixTransform
  _trns: function(type, m) {
    return [
      '<', type,
      '.RenderTransform><MatrixTransform><MatrixTransform.Matrix><Matrix M11="',
                 m[0], '" M21="', m[3], '" OffsetX="', m[6],
      '" M12="', m[1], '" M22="', m[4], '" OffsetY="', m[7],
      '" /></MatrixTransform.Matrix></MatrixTransform></', type,
      '.RenderTransform>'].join("");
  },

  // build Shadow Blur
  _blur: function(type, shadowColor) {
    var sdepth = 0,
        sx = this[SHADOW_OFFSET_X],
        sy = this[SHADOW_OFFSET_Y];

    if (shadowColor[1]) {
      sdepth = _max(_math.abs(sx), _math.abs(sy)) * 1.2;
      return ['<', type, '.Effect><DropShadowEffect Opacity="', 1.0,
              '" Color="', shadowColor[0],
              '" BlurRadius="', this[SHADOW_BLUR] * 1.2,
              '" Direction="', _math.atan2(-sy, sx) * _deg,
              '" ShadowDepth="', sdepth,
              '" /></', type, '.Effect>'].join("");
    }
    return "";
  }
});

// --- VML ---
function VMLInit(elm) {
  var e = removeFallbackContents(elm);

  applyCanvasSize(e);
  e.getContext = function() { return e._ctx2d; }
  e._ctx2d = new VML2D(e);
  e.bind = function() {
    e.attachEvent("onpropertychange", onPropertyChange);
  };
  e.unbind = function() {
    e.detachEvent("onpropertychange", onPropertyChange);
  };
  e.bind();
  return e;
}

function VML2D(elm) {
  this.canvas = elm;
  elm.uuCanvasType = "VML2D";
  this._initSurface();
  this._elm = elm.appendChild(_doc.createElement("div"));
  this._elm.style.pixelWidth = elm.width;
  this._elm.style.pixelHeight = elm.height;
  this._elm.style.overflow = "hidden";
  this._elm.style.position = "absolute";
  this._elm.uuCanvasDirection = elm.currentStyle.direction;
  this._elm.style.direction = "ltr";
  this._clipRect = this._rect(0, 0, this.canvas.width, this.canvas.height);
};

_mix(VML2D.prototype, _super, {
  _rect: function(x, y, w, h) {
    var c0 = this._map(x, y),
        c1 = this._map(x + w, y),
        c2 = this._map(x + w, y + h),
        c3 = this._map(x, y + h);
    return [" m", c0.x, " ", c0.y,
            " l", c1.x, " ", c1.y,
            " l", c2.x, " ", c2.y,
            " l", c3.x, " ", c3.y,
            " x"].join("");
  },

  _map: function(x, y) {
    var m = this._mtx;
    return { x: _round((x * m[0] + y * m[3] + m[6]) * _zoom - _halfZoom),
             y: _round((x * m[1] + y * m[4] + m[7]) * _zoom - _halfZoom) };
  },

  // === State =============================================
  // === Transformations ===================================
  // === Rects =============================================
  clearRect: function(x, y, w, h) {
    w = _int(w), h = _int(h);
    if ((!x && !y &&
         w == this.canvas.width &&
         h == this.canvas.height)) {
      this._clear();
    } else {
      var zindex = 0, c = _style.getBGColor(this._elm, 1), vml;

      switch (COMPOSITES[this[GLOBAL_COMPO]]) {
      case  4: zindex = --this._zindex; break;
      case 10: this._clear();
      }

      vml =  [VML_SHAPE_STYLE, zindex,
              VML_FILL, VML_COORD, VML_PATH, this._rect(x, y, w, h),
              VML_VFILL, VML_TYPE_HEAD, 'solid',
              VML_COLOR, c[0], VML_OPACITY, c[1] * this[GLOBAL_ALPHA],
              VML_END_SHAPE].join("");
      !this.xFlyweight &&
        this._history.push(this._clipPath ? (vml = this._clippy(vml)) : vml);
      this._elm.insertAdjacentHTML("BeforeEnd", vml);
    }
  },

  _clear: function() {
    this._history = [];
    this._elm.innerHTML = ""; // clear all
    this._zindex = 0;
  },

  fillRect: function(x, y, w, h) {
    var path = this._rect(x, y, w, h);
    this._px = x;
    this._py = y;

    // When all canvases are painted out,
    // the fillStyle(background-color) is preserved.
    if (path === this._clipRect) { // full size path
      if (typeof this[FILL_STYLE] === "string") {
        this.xClipStyle = this[FILL_STYLE]; // keep bgcolor
      }
    }
    this.fill(0, path);
  },

  // === Path API ==========================================
  closePath: function() {
    this._path.push(" x");
  },

  moveTo: function(x, y) {
    var m = this._mtx; // inlining: this._map(x, y)
    this._path.push(
      "m ", _round((x * m[0] + y * m[3] + m[6]) * _zoom - _halfZoom), " ",
            _round((x * m[1] + y * m[4] + m[7]) * _zoom - _halfZoom));
    this._px = x;
    this._py = y;
  },

  lineTo: function(x, y) {
    var m = this._mtx; // inlining: this._map(x, y)
    this._path.push(
      "l ", _round((x * m[0] + y * m[3] + m[6]) * _zoom - _halfZoom), " ",
            _round((x * m[1] + y * m[4] + m[7]) * _zoom - _halfZoom));
    this._px = x;
    this._py = y;
  },

  quadraticCurveTo: function(cpx, cpy, x, y) {
    var cp1x = this._px + 2.0 / 3.0 * (cpx - this._px),
        cp1y = this._py + 2.0 / 3.0 * (cpy - this._py),
        cp2x = cp1x + (x - this._px) / 3.0,
        cp2y = cp1y + (y - this._py) / 3.0,
        c0 = this._map(x, y),
        c1 = this._map(cp1x, cp1y),
        c2 = this._map(cp2x, cp2y);
    this._path.push("c ", c1.x, " ", c1.y, " ",
                          c2.x, " ", c2.y, " ",
                          c0.x, " ", c0.y);
    this._px = x;
    this._py = y;
  },

  bezierCurveTo: function(cp1x, cp1y, cp2x, cp2y, x, y) {
    var c0 = this._map(x, y),
        c1 = this._map(cp1x, cp1y),
        c2 = this._map(cp2x, cp2y);
    this._path.push("c ", c1.x, " ", c1.y, " ",
                          c2.x, " ", c2.y, " ",
                          c0.x, " ", c0.y);
    this._px = x;
    this._py = y;
  },

  rect: function(x, y, w, h) {
    this._path.push(this._rect(x, y, w, h));
    this._px = x;
    this._py = y;
  },

  arc: function(x, y, radius, startAngle, endAngle, anticlockwise) {
    radius *= _zoom;
    var x1 = x + (_cos(startAngle) * radius) - _halfZoom,
        y1 = y + (_sin(startAngle) * radius) - _halfZoom,
        x2 = x + (_cos(endAngle)   * radius) - _halfZoom,
        y2 = y + (_sin(endAngle)   * radius) - _halfZoom,
        c0, c1, c2, rx, ry;

    if (!anticlockwise) {
      // fix "wa" bug
      (x1.toExponential(5) === x2.toExponential(5)) && (x1 += 0.125);
      (y1.toExponential(5) === y2.toExponential(5)) && (y1 += 0.125);
    }
    c0 = this._map(x, y),
    c1 = this._map(x1, y1),
    c2 = this._map(x2, y2),
    rx = this._scaleX * radius,
    ry = this._scaleY * radius;
    this._path.push(anticlockwise ? "at " : "wa ",
                    c0.x - rx, " ", c0.y - ry, " ",
                    c0.x + rx, " ", c0.y + ry, " ",
                    c1.x, " ", c1.y, " ",
                    c2.x, " ", c2.y);
  },

  fill: function(wire, path) {
    path = path || this._path.join("");

    var rv = [], vml, zindex = 0, mix, c,
        style = wire ? this[STROKE_STYLE] : this[FILL_STYLE],
        // for shadow
        si = 0, so = 0, sd = 0, sx = 0, sy = 0,
        sc = _colorCache[this[SHADOW_COLOR]] ||
             _parseColor(this[SHADOW_COLOR]);

    if ( (mix = COMPOSITES[this[GLOBAL_COMPO]]) ) {
      (mix === 4) ? (zindex = --this._zindex) : this._clear();
    }

    if (typeof style === "string") {
      c = _colorCache[style] || _parseColor(style);

      if (sc[1]) {
        sx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
        sy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
        so = this.xShadowOpacityFrom;
        sd = this.xShadowOpacityDelta;

        for (; si < _shadowWidth; so += sd, --sx, --sy, ++si) {
          rv.push(VML_SHAPE_STYLE, zindex,
                  ';left:', sx,
                  'px;top:', sy, 'px',
                  wire ? VML_STROKE : VML_FILL,
                  VML_COORD, VML_PATH, path,
                  wire ? VML_VSTROKE : VML_VFILL,
                  VML_COLOR_HEAD, sc[0],
                  VML_OPACITY, so.toFixed(2),
                  wire ? strokeProps(this, 1) : "",
                  VML_END_SHAPE);
        }
      }

      rv.push(VML_SHAPE_STYLE, zindex,
              wire ? VML_STROKE : VML_FILL,
              VML_COORD, VML_PATH, path,
              wire ? VML_VSTROKE : VML_VFILL,
              VML_COLOR_HEAD, c[0],
              VML_OPACITY, c[1] * this[GLOBAL_ALPHA],
              wire ? strokeProps(this, 1) : "",
              VML_END_SHAPE);

      vml = rv.join("");
    } else {
      vml = this[FUNCS[style._type]](style, path, wire, mix, zindex, sc);
    }
    !this.xFlyweight &&
      this._history.push(this._clipPath ? (vml = this._clippy(vml)) : vml);
    this._elm.insertAdjacentHTML("BeforeEnd", vml);
  },

  _lfill: function(style, path, wire, mix, zindex, shadowColor) {
    var rv = [],
        fp = style._param,
        c0 = this._map(fp.x0, fp.y0),
        c1 = this._map(fp.x1, fp.y1),
        angle = _math.atan2(c1.x - c0.x, c1.y - c0.y) * _deg,
        color = this._gcolor(style._colorStop),
        // for shadow
        si = 0, siz = _shadowWidth, so = 0, sd = 0, sx = 0, sy = 0;

    (angle < 0) && (angle += 360);

    if (shadowColor[1]) {
      sx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
      sy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
      so = this.xShadowOpacityFrom;
      sd = this.xShadowOpacityDelta;

      if (wire) {
        siz = this[LINE_WIDTH];
        sd = 0.2 / siz; // opacity from 0.05 to 0.25
      }
      for (; si < siz; so += sd, --sx, --sy, ++si) {
        rv.push(VML_SHAPE_STYLE, zindex,
                ';left:', sx, 'px;top:', sy, 'px',
                VML_COORD, wire ? VML_STROKE : VML_FILL,
                VML_PATH, path,
                  // brush
                  wire ? VML_VSTROKE : VML_VFILL,
                  wire ? VML_FILLTYPE_HEAD : VML_TYPE_HEAD,
                  wire ? 'solid' : 'gradient" method="sigma" focus="0%',
                  VML_COLOR, shadowColor[0],
                  VML_OPACITY, so.toFixed(2),
                  VML_ANGLE, angle,
                  wire ? strokeProps(this, 1) : "",
                VML_END_SHAPE);
      }
    }
    rv.push(VML_SHAPE_STYLE, zindex,
            VML_COORD, wire ? VML_STROKE : VML_FILL,
            VML_PATH, path,
              // brush
              wire ? VML_VSTROKE : VML_VFILL,
              wire ? VML_FILLTYPE_HEAD : VML_TYPE_HEAD,
              wire ? 'solid' : 'gradient" method="sigma" focus="0%',
              wire ? VML_COLOR : VML_COLORS,
              wire ? _parseColor(this.xMissColor)[0] : color,
              VML_OPACITY, this[GLOBAL_ALPHA],
              '" o:opacity2="', this[GLOBAL_ALPHA], // fill only
              VML_ANGLE, angle,
              wire ? strokeProps(this, 1) : "",
            VML_END_SHAPE);
    return rv.join("");
  },

  _rfill: function(style, path, wire, mix, zindex, shadowColor) {
    var rv = [], brush, v,
        fp = style._param, fsize, fposX, fposY, focusParam = "",
        color = this._gcolor(style._colorStop),
        zindex2 = 0,
        x = fp.x1 - fp.r1,
        y = fp.y1 - fp.r1,
        r1x = fp.r1 * this._scaleX,
        r1y = fp.r1 * this._scaleY,
        c0 = this._map(x, y),
        // for shadow
        si = 0, siz = _shadowWidth, so = 0, sd = 0, sx = 0, sy = 0;

    // focus
    if (!wire) {
      fsize = (fp.r0 / fp.r1);
      fposX = (1 - fsize + (fp.x0 - fp.x1) / fp.r1) / 2; // forcus position x
      fposY = (1 - fsize + (fp.y0 - fp.y1) / fp.r1) / 2; // forcus position y
    }

    if (shadowColor[1]) {
      sx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
      sy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
      so = this.xShadowOpacityFrom;
      sd = this.xShadowOpacityDelta;

      if (wire) {
        siz = this[LINE_WIDTH];
        sd = 0.2 / siz; // opacity from 0.05 to 0.25
      }

      if (wire) {
        focusParam = [VML_VSTROKE, VML_FILLTYPE_HEAD, 'tile',
                      strokeProps(this, 1)].join("");
      } else {
        focusParam = [VML_VFILL, VML_TYPE_HEAD,
                      'gradientradial" method="sigma" focussize="',
                      fsize, ',', fsize,
                      '" focusposition="', fposX, ',', fposY].join("");
      }
      for (; si < siz; so += sd, --sx, --sy, ++si) {
        rv.push('<v:oval', VML_BASE_STYLE, zindex,
                ';left:', _round(c0.x / _zoom) + sx,
                'px;top:', _round(c0.y / _zoom) + sy,
                'px;width:', r1x, 'px;height:', r1y,
                'px', wire ? VML_STROKE : VML_FILL,
                '" coordsize="11000,11000',
                focusParam, VML_OPACITY, so.toFixed(2),
                VML_COLOR, shadowColor[0],
                '" /></v:oval>');
      }
    }

    if (wire) {
      // VML has not stroke gradient
      brush = [VML_VSTROKE, VML_FILLTYPE_HEAD, 'tile', strokeProps(this, 1),
               VML_OPACITY, this[GLOBAL_ALPHA],
               VML_COLOR, _parseColor(this.xMissColor)[0]].join("");
    } else {
      // fill outside
      if (style._colorStop.length) {
        v = style._colorStop[0]; // 0 = outer color
        if (v.color[1] > 0.001) {
          if (mix === 4) { zindex2 = --this._zindex; }
          rv.push(VML_SHAPE_STYLE, zindex2,
                  VML_FILL, VML_COORD, VML_PATH, path,
                  VML_VFILL, VML_TYPE_HEAD, 'solid',
                  VML_COLOR, v.color[0],
                  VML_OPACITY, v.color[1] * this[GLOBAL_ALPHA],
                  VML_END_SHAPE);
        }
      }
      brush = [VML_VFILL, VML_TYPE_HEAD,
               'gradientradial" method="sigma" focussize="',
               fsize , ',', fsize,
               '" focusposition="', fposX, ',', fposY,
               VML_OPACITY, this[GLOBAL_ALPHA],
               '" o:opacity2="', this[GLOBAL_ALPHA],
               VML_COLORS, color].join("");
    }
    rv.push('<v:oval', VML_BASE_STYLE, zindex, // need z-index
            ';left:', _round(c0.x / _zoom),
            'px;top:', _round(c0.y / _zoom),
            'px;width:', r1x, 'px;height:', r1y, 'px',
            wire ? VML_STROKE : VML_FILL,
            '" coordsize="11000,11000', brush,
            '" /></v:oval>');
    return rv.join("");
  },

  _pfill: function(style, path, wire, mix, zindex, shadowColor) {
    var rv = [],
        // for shadow
        si = 0, so = 0, sd = 0, sx = 0, sy = 0;

    if (shadowColor[1]) {
      sx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
      sy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
      so = this.xShadowOpacityFrom;
      sd = this.xShadowOpacityDelta;

      for (; si < _shadowWidth; so += sd, --sx, --sy, ++si) {
        rv.push(VML_SHAPE_STYLE, zindex,
                ';left:', sx, 'px;top:', sy, 'px',
                VML_COORD,
                wire ? VML_STROKE : VML_FILL,
                VML_PATH, path,
                  // brush
                  wire ? VML_VSTROKE: VML_VFILL,
                  wire ? VML_FILLTYPE_HEAD : VML_TYPE_HEAD, 'solid',
                  wire ? strokeProps(this, 1) : "",
                  VML_COLOR, shadowColor[0],
                  VML_OPACITY, so.toFixed(2),
                VML_END_SHAPE);
      }
    }

    rv.push(VML_SHAPE_STYLE, zindex,
            VML_COORD,
            wire ? VML_STROKE : VML_FILL,
            VML_PATH, path,
              // brush
              wire ? VML_VSTROKE : VML_VFILL,
              wire ? VML_FILLTYPE_HEAD : VML_TYPE_HEAD, 'tile',
              VML_OPACITY, this[GLOBAL_ALPHA],
              '" src="', style._src,
              wire ? strokeProps(this, 1) : "",
            VML_END_SHAPE);

    return rv.join("");
  },

  clip: function() {
    this._clipPath = this._clipRect + " x " + this._path.join("");
  },

  _clippy: function(vml) {
    if (!this.xClipStyle) {
      var bg = _style.getBGColor(this._elm, 1);
      this.xClipStyle = bg[0];
    }
    return [vml, '<v:shape style="position:absolute;width:10px;height:10px',
            VML_FILL, VML_COORD, VML_PATH, this._clipPath,
            VML_VFILL, VML_TYPE_HEAD, 'solid', VML_COLOR, this.xClipStyle,
            VML_END_SHAPE].join("");
  },

  // === Text ==============================================
  fillText: function(text, x, y, maxWidth, wire) {
    text = text.replace(/(\t|\v|\f|\r\n|\r|\n)/g, " ");
    var style = wire ? this[STROKE_STYLE] : this[FILL_STYLE],
        types = (typeof style === "string") ? 0 : style._type,
        rv = [], vml, align = this[TEXT_ALIGN], dir = "ltr", c,
        font = parseFont(this[FONT], this.canvas),
        m = this._mtx, zindex = 0,
        fp, c0, c1, // for grad
        skew = [m[0].toFixed(3) + ',' + m[3].toFixed(3) + ',' +
                m[1].toFixed(3) + ',' + m[4].toFixed(3) + ',0,0'].join(""),
        skewOffset,
        delta = 1000, left = 0, right = delta,
        offset = { x: 0, y: 0 },
        // for shadow
        si = 0, so = 0, sd = 0, sx = 0, sy = 0,
        sc = _colorCache[this[SHADOW_COLOR]] ||
             _parseColor(this[SHADOW_COLOR]);

    switch (COMPOSITES[this[GLOBAL_COMPO]]) {
    case  4: zindex = --this._zindex; break;
    case 10: this._clear();
    }

    switch (align) {
    case "end": dir = "rtl"; // break;
    case "start":
      align = this._elm.uuCanvasDirection === dir ? "left" : "right"
    }
    switch (align) {
    case "center": left = right = delta / 2; break;
    case "right": left = delta, right = 0.05;
    }
    if (this[TEXT_BASELINE] === "top") {
      // text margin-top fine tuning
      offset.y = font.size /
          (FONT_SCALES[font.rawfamily.split(",")[0].toUpperCase()] ||
           this.xTextMarginTop);
    }
    skewOffset = this._map(x + offset.x, y + offset.y);

    if (sc[1] && !this.xShadowBlur) {
      sx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
      sy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
      so = _max(this.xShadowOpacityFrom + 0.9, 1);
      sd = this.xShadowOpacityDelta;

      for (; si < _shadowWidth; so += sd, --sx, --sy, ++si) {
        rv.push('<v:line',
                VML_BASE_STYLE, zindex, ';width:1px;height:1px;left:', sx,
                'px;top:', sy, 'px',
                VML_FILL, '" from="', -left, ' 0" to="', right,
                ' 0.05" coordsize="100 100">',
                '<v:fill color="', sc[0],
                '" opacity="', so.toFixed(2), '" />',
                '<v:skew on="t" matrix="', skew ,'" ',
                ' offset="', _round(skewOffset.x / _zoom), ',',
                             _round(skewOffset.y / _zoom),
                '" origin="', left ,' 0" />',
                '<v:path textpathok="t" />',
                '<v:textpath on="t" string="', toHTMLEntity(text),
                '" style="v-text-align:', align,
                ';font:', toHTMLEntity(font.formal),
                '" /></v:line>');
      }
    }

    rv.push('<v:line',
            VML_BASE_STYLE, zindex, ';width:1px;height:1px',
            VML_FILL, '" from="', -left, ' 0" to="', right,
            ' 0.05" coordsize="100 100">');

    switch (types) {
    case 0:
      c = _colorCache[style] || _parseColor(style);
      rv.push('<v:fill color="', c[0],
              '" opacity="', c[1] * this[GLOBAL_ALPHA], '" />');
      break;
    case 1:
    case 2:
      fp = style._param;
      c0 = this._map(fp.x0, fp.y0);
      c1 = this._map(fp.x1, fp.y1);
      rv.push('<v:fill type="gradient" method="sigma" focus="0%',
              VML_COLORS, this._gcolor(style._colorStop),
              VML_OPACITY, this[GLOBAL_ALPHA],
              '" o:opacity2="', this[GLOBAL_ALPHA],
              VML_ANGLE,
              _math.atan2(c1.x - c0.x, c1.y - c0.y) * _deg,
              '" />');
      break;
    case 3:
      rv.push('<v:fill position="0,0" type="tile" src="',
              style._src, '" />');
      break;
    }
    rv.push('<v:skew on="t" matrix="', skew ,'" ',
            ' offset="', _round(skewOffset.x / _zoom), ',',
                         _round(skewOffset.y / _zoom),
            '" origin="', left ,' 0" />',
            '<v:path textpathok="t" />',
            '<v:textpath on="t" string="', toHTMLEntity(text),
            '" style="v-text-align:', align,
            ';font:', toHTMLEntity(font.formal),
            '" /></v:line>');
    vml = rv.join("");
    !this.xFlyweight &&
      this._history.push(this._clipPath ? (vml = this._clippy(vml)) : vml);
    this._elm.insertAdjacentHTML("BeforeEnd", vml);
  },
  // drawing images
  // drawImage(image, dx, dy)
  // drawImage(image, dx, dy, dw, dh)
  // drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
  drawImage: function(image) {
    var info = detectDrawImageArg.apply(this, arguments),
        method = info.az === 3 ? "image" : "scale",
        dx = info.dx,
        dy = info.dy,
        dw = info.dw,
        dh = info.dh,
        sx = info.sx,
        sy = info.sy,
        sw = info.sw,
        sh = info.sh,
        iw = info.dim.w,
        ih = info.dim.h,
        rv = [], vml, m,
        frag = [], sfrag, tfrag, // code fragment
        i = 0, iz, me = this, c0, zindex = 0,
        ieMode8 = _doc.documentMode >= 8,
        prefix = ieMode8 ? "-ms-filter:'" : "filter:", // filter prefix
        postfix = ieMode8 ? "'" : "",
        sizeTrans, // 0: none size transform, 1: size transform
        // for shadow
        si = 0, so = 0, sd = 0, shx = 0, shy = 0, shw = _shadowWidth,
        sc = _colorCache[this[SHADOW_COLOR]] ||
             _parseColor(this[SHADOW_COLOR]);

    function trans(m, x, y, w, h) {
      var c1 = me._map(x, y),
          c2 = me._map(x + w, y),
          c3 = me._map(x + w, y + h),
          c4 = me._map(x, y + h);
      return [";padding:0 ",
              _round(_max(c1.x, c2.x, c3.x, c4.x) / _zoom), "px ",
              _round(_max(c1.y, c2.y, c3.y, c4.y) / _zoom), "px 0;",
              prefix, DX_PFX, ".Matrix(M11=", m[0], ",M12=", m[3],
                ",M21=", m[1], ",M22=", m[4],
                ",Dx=", _round(c1.x / _zoom),
                ",Dy=", _round(c1.y / _zoom), ")", postfix].join("");
    }

    switch (COMPOSITES[this[GLOBAL_COMPO]]) {
    case  4: zindex = --this._zindex; break;
    case 10: this._clear();
    }

    if ("src" in image) { // image is HTMLImageElement
      c0 = this._map(dx, dy);

      if (this.xImageRender) {
        rv.push(
          '<v:image', VML_BASE_STYLE, zindex,
          ';width:',    dw,
          'px;height:', dh,
          'px;left:', _round(c0.x / _zoom),
          'px;top:',  _round(c0.y / _zoom),
          'px" coordsize="100,100',
          '" src="', image.src,
          '" cropleft="',   sx / iw,
          '" croptop="',    sy / ih,
          '" cropright="',  (iw - sx - sw) / iw,
          '" cropbottom="', (ih - sy - sh) / ih,
          '" />');
      } else {
        sizeTrans = (sx || sy); // 0: none size transform, 1: size transform
        tfrag = this._efx ? trans(this._mtx, dx, dy, dw, dh) : '';

        frag = [
          // shadow only
          [ '<div', VML_BASE_STYLE, zindex - 10,
              ';left:$1px;top:$2px', tfrag, '">'].join(""),
          [ '<div style="position:relative;overflow:hidden;width:',
              _round(dw), 'px;height:', _round(dh), 'px">'].join(""),
          !sizeTrans ? "" : [
            '<div style="width:', _ceil(dw + sx * dw / sw),
              'px;height:', _ceil(dh + sy * dh / sh),
              'px;',
              prefix, DX_PFX,
              '.Matrix(Dx=', (-sx * dw / sw).toFixed(3),
                     ',Dy=', (-sy * dh / sh).toFixed(3), ')',
              postfix, '">'].join(""),
          [ '<div style="width:', _round(iw * dw / sw),
              'px;height:', _round(ih * dh / sh),
              'px;'].join(""),
          // shadow only
          [ 'background-color:', sc[0], ';',
            prefix, DX_PFX, '.Alpha(opacity=$3)', postfix].join(""),
          // alphaloader
          [ prefix, DX_PFX, '.AlphaImageLoader(src=',
            image.src, ',SizingMethod=',
            method, ')', postfix].join(""),
          [ '"></div>',
              sizeTrans ? '</div>' : '', '</div></div>'].join("")
        ];

        if (sc[1]) {
          shx = shw / 2 + this[SHADOW_OFFSET_X];
          shy = shw / 2 + this[SHADOW_OFFSET_Y];
          so = this.xShadowOpacityFrom;
          sd = this.xShadowOpacityDelta;

          sfrag = [frag[0], frag[1], frag[2], frag[3],
                   frag[4], frag[6]].join("");
          for (; si < shw; so += sd, --shx, --shy, ++si) {
            rv.push(
              sfrag.replace(/\$1/, this._efx ? shx : _round(c0.x / _zoom) + shx)
                   .replace(/\$2/, this._efx ? shy : _round(c0.y / _zoom) + shy)
                   .replace(/\$3/, (so * 100).toFixed(2)));
          }
        }

        rv.push('<div', VML_BASE_STYLE, zindex);
        if (this._efx) {
          rv.push(tfrag, '">');
        } else { // 1:1 scale
          rv.push(';top:', _round(c0.y / _zoom),
                  'px;left:', _round(c0.x / _zoom), 'px">')
        }
        rv.push(frag[1], frag[2], frag[3], frag[5], frag[6]);
      }
      vml = rv.join("");
    } else {
      c0 = this._map(dx, dy);
      switch (info.az) {
      case 3: // 1:1 scale
              rv.push('<div', VML_BASE_STYLE, zindex,
                      ';left:', _round(c0.x / _zoom),
                      'px;top:', _round(c0.y / _zoom), 'px">')
              iz = image._ctx2d._history.length;

              for (; i < iz; ++i) {
                rv.push(image._ctx2d._history[i]);
              }
              rv.push('</div>');
              break;
      case 5:
              m = _matrix.multiply(_matrix.scale(dw / iw, dh / ih), this._mtx);
              rv.push('<div', VML_BASE_STYLE, zindex,
                      trans(m, dx, dy, dw, dh),
                      '"><div style="width:',  _round(iw * dw / sw),
                                 'px;height:', _round(ih * dh / sh), 'px">');
              iz = image._ctx2d._history.length;

              for (; i < iz; ++i) {
                rv.push(image._ctx2d._history[i]);
              }
              rv.push('</div></div>');
              break;
      case 9: // buggy(not impl)
              m = _matrix.multiply(_matrix.scale(dw / sw, dh / sh), this._mtx);
  //          m = _matrix.multiply(_matrix.translate(dx, dy), m);
              rv.push('<div', VML_BASE_STYLE, zindex,
                      ';overflow:hidden',
                      trans(m, dx, dy, dw, dh), '">');

              iz = image._ctx2d._history.length;

              for (; i < iz; ++i) {
                rv.push(image._ctx2d._history[i]);
              }
              rv.push('</div>');
              break;
      }
      vml = rv.join("");
/*
      // effect CSS::opacity and filter::opacity
      vml = vml.replace(/opacity=\"([\d\.]+)\"/g, function(m, opa) {
        return 'opacity="' + (opa * me[GLOBAL_ALPHA]).toFixed(3) + '"';
      }).replace(/opacity=([\d\.]+)/g, function(m, opa) {
        return 'opacity=' + (opa * me[GLOBAL_ALPHA]).toFixed(3);
      });
 */
    }
    !this.xFlyweight &&
      this._history.push(this._clipPath ? (vml = this._clippy(vml)) : vml);
    this._elm.insertAdjacentHTML("BeforeEnd", vml);
  },

  // === Pixel manipulation ================================
  // === Gradient ==========================================
  createLinearGradient: function(x0, y0, x1, y1) {
    return new Grad(1, // 1:gradient
                    { x0: x0, y0: y0, x1: x1, y1: y1 }, 1);
  },

  createRadialGradient: function(x0, y0, r0, x1, y1, r1) {
    return new Grad(2, // 2:gradientradial
                    { x0: x0, y0: y0, r0: r0, x1: x1, y1: y1, r1: r1 }, 1);
  },

  createPattern: function(image, repetition) {
    return new Patt(image, repetition);
  },

  // build Gradation Color
  _gcolor: function(ary) {
    var rv = [], i = 0, iz = ary.length;
    for (; i < iz; ++i) {
      rv.push(ary[i].offset + " " + ary[i].color[0]);
    }
    return rv.join(",");
  }
});

// === Extend Test, Shadow API =============================
if (CANVAS_RENDERING_CONTEXT_2D in _win) { // include Safari4
  _crc2d = _win[CANVAS_RENDERING_CONTEXT_2D].prototype;
}

if (_crc2d &&
    ((_gecko  && _egver <= 1.9) || // Firefox2-3
     (_opera  && _uaver <= 10))) { // Opera9.2-10
                                   // exclude Chrome1, Safari3.x
  // wrapper
  _crc2d._save = _crc2d.save;
  _crc2d._restore = _crc2d.restore;
  _crc2d._clearRect = _crc2d.clearRect;

  _mix(_crc2d, {
    _shadow:      [TRANSPARENT, 0, 0, 0],
    _stack:       [],
    font:         "10px sans-serif", // for Firefox3
    textAlign:    "start",
    textBaseline: "top",    // spec: "alphabetic"
    xMissColor:           "#000",
    xTextMarginTop:       1.3,
    xAutoTextRender:      1, // 1 = auto;
    xShadowOpacityFrom:   0.01, // for Silverlight, VML
    xShadowOpacityDelta:  0.05, // for Silverlight, VML

    save: function() {
      this._stack.push([this[FONT],
                        this[TEXT_ALIGN],
                        this[TEXT_BASELINE],
                        _mix([], this._shadow)]);
      this._save();
    },
    restore: function() {
      this._restore();
      if (this._stack.length) { // for Opera9.5+, Firefox2, Firefox3
        var last = this._stack.pop();
        this[FONT] = last[0];
        this[TEXT_ALIGN] = last[1];
        this[TEXT_BASELINE] = last[2];
        this._shadow = last[3];
      }
    },
    clearRect: function(x, y, w, h) {
      var fn = clearRectDOM;
      if (this.xAutoTextRender) {
        if (_gecko && _egver === 1.9) {
          fn = clearRectMoz;
        } else if (_opera && _uaver >= 9.5 && _uaver <= 10) {
          fn = clearRectSVG;
        }
      }
      fn(this, x, y, w, h);
    },
    fillText: function(text, x, y, maxWidth, wire) {
      var fn = fillTextDOM;
      if (this.xAutoTextRender) {
        if (_gecko && _egver === 1.9) {
          fn = fillTextMoz;
        } else if (_opera && _uaver >= 9.5 && _uaver <= 10) {
          fn = fillTextSVG;
        }
      }
      fn(this, text, x, y, maxWidth, wire);
    },
    strokeText: function(text, x, y, maxWidth) {
      this.fillText(text, x, y, maxWidth, 1);
    },
    measureText: function(text) {
      var metric = getTextMetric(text, this[FONT]);
      return new TextMetrics(metric.w, metric.h);
    }
  });

  // Extend Shadow Accesser
  if (_gecko && _egver <= 1.9) { // Firefox2-3
    _crc2d.__defineSetter__(SHADOWS[0], function(c) { this._shadow[0] = c; });
    _crc2d.__defineSetter__(SHADOWS[1], function(x) { this._shadow[1] = x; });
    _crc2d.__defineSetter__(SHADOWS[2], function(y) { this._shadow[2] = y; });
    _crc2d.__defineSetter__(SHADOWS[3], function(b) { this._shadow[3] = b; });
    _crc2d.__defineGetter__(SHADOWS[0], function() { return this._shadow[0]; });
    _crc2d.__defineGetter__(SHADOWS[1], function() { return this._shadow[1]; });
    _crc2d.__defineGetter__(SHADOWS[2], function() { return this._shadow[2]; });
    _crc2d.__defineGetter__(SHADOWS[3], function() { return this._shadow[3]; });
  }
}

if (_crc2d &&
    (_chrome && _uaver === 2)) { // Chrome3 strokeText() implemented

  _crc2d.strokeText = function(text, x, y, maxWidth) {
    this.save();
    this[FILL_STYLE] = this[STROKE_STYLE];
    this.fillText(text, x, y, maxWidth);
    this.restore();
  }
}

function clearTextView(me) {
  var i = 1, iz = me.canvas._canvasTextView.length;
  for (; i < iz; ++i) {
    me.canvas._canvasTextView[i].textContent = "";
  }
}

function clearRectMoz(me, x, y, w, h) {
  me._clearRect(x, y, w, h);
}

function clearRectDOM(me, x, y, w, h) {
  if (me.canvas._canvasTextView &&
      !x && !y && w == me.canvas.width && h == me.canvas.height) {
    clearTextView(me);
  }
  me._clearRect(x, y, w, h);
}

function clearRectSVG(me, x, y, w, h) {
  me._clearRect(x, y, w, h);
}

function fillTextMoz(me, text, x, y, maxWidth, wire) {
  var align = me[TEXT_ALIGN], dir = "ltr",
      metric = getTextMetric(text, me[FONT]),
      offX = 0, offY = 0,
      // for shadow
      si = 0, so = 0, sd = 0,
      sc = _colorCache[me._shadow[0]] ||
           _parseColor(me._shadow[0]);

  switch (align) {
  case "end": dir = "rtl"; // break;
  case "start":
    align = _runstyle(me.canvas, "").direction === dir ? "left" : "right"
  }
  if (align === "center") {
    offX = metric.w / 2;
  } else if (align === "right") {
    offX = metric.w;
  }
  offY = (metric.h + metric.h / 2) / 2; // emulate textBaseline="top"

  me.save();
  me[GLOBAL_COMPO] = "source-over";
  me.mozTextStyle = me.font;
  me.translate(x - offX, y + offY);
  if (wire) {
    me[FILL_STYLE] = me[STROKE_STYLE];
  }

  if (sc[1]) {
    so = _max(me.xShadowOpacityFrom + 0.9, 1);
    sd = me.xShadowOpacityDelta;

    me.save();
    me.translate(_shadowWidth / 2 + me._shadow[1],
                 _shadowWidth / 2 + me._shadow[2]);
    for (; si < _shadowWidth; so += sd, ++si) {
      me.translate(-1, -1);
      me[GLOBAL_ALPHA] = so.toFixed(2);
      me[FILL_STYLE] = sc[0];
      me.mozDrawText(text);
    }
    me.restore();
  }

  me.mozDrawText(text);
  // http://d.hatena.ne.jp/uupaa/20090506/1241572019
  me.fillRect(0,0,0,0); // force redraw(Firefox3.0 bug)
  me.restore();
}

function fillTextDOM(me, text, x, y, maxWidth, wire) {
  var canvas = me.canvas, // HTMLCanvasElement
      view, layer, sc, name,
      offX = 0, metric, align = me[TEXT_ALIGN], dir = "ltr";

  if (canvas._canvasLayerView) {
    view = canvas._canvasLayerView._view;
    canvas._canvasTextView = [view];
  } else if (!canvas._canvasTextView) {
//  view = canvas.parentNode.appendChild(_doc.createElement("div"));
    view = _doc.body.appendChild(_doc.createElement("div"));
    view.style.position = "absolute";
    view.style.overflow = "hidden";
    canvas._canvasTextView = [view];

    // reposition
    function repos(attr) {
      function getPos(elm) {
        var x = 0, y = 0, r;
        if (elm.getBoundingClientRect) {
          r = elm.getBoundingClientRect();
          x = r.left + pageXOffset;
          y = r.top  + pageYOffset;
        } else {
          while (elm) {
            x += elm.offsetLeft || 0;
            y += elm.offsetTop  || 0;
            elm = elm.offsetParent;
          }
        }
        return { x: x, y: y };
      }

      try {
        var rect, style = _runstyle(me.canvas, "");
        if (attr & 1) {
          rect = getPos(me.canvas);
        } else {
          rect = { x: _int(style.left), y: _int(style.top) };
        }
        _mix(me.canvas._canvasTextView[0].style, {
//        zIndex: (_int(style.zIndex) || 0) + 1, // Fx2"auto" -> 1
          height: _int(canvas.height) + "px",
          width: _int(canvas.width) + "px",
          left: rect.x + "px",
          top: rect.y + "px",
          visibility: style.visibility,
          display: style.display,
          opacity: _float(style.opacity)
        });
        if (!_gecko) {
          _mix(me.canvas._canvasTextView[0].style, {
            zIndex: (_int(style.zIndex) || 0) + 1
          });
        }
      } catch (err) {}
    }
    function onAttr(evt) {
      var attr = HIT_PROPS2[evt.attrName] || 0;
      if (attr) {
        (attr & 1) && clearTextView(me); // clear
        repos(attr);
      }
    }
    repos(3);
    canvas.addEventListener("DOMAttrModified", onAttr, false);
    setInterval(function() { repos(3); }, 1000); // delay 1sec
  } else {
    view = canvas._canvasTextView[0];
  }
  // Firefox2: shadowColor is always null
  if (_gecko) {
    sc = _colorCache[me._shadow[0]] ||
         _parseColor(me._shadow[0]);
  } else {
    sc = _colorCache[me[SHADOW_COLOR]] ||
         _parseColor(me[SHADOW_COLOR]);
  }

  metric = getTextMetric(text, me[FONT]);
  switch (align) {
  case "end": dir = "rtl"; // break;
  case "start":
    align = _runstyle(me.canvas, "").direction === dir ? "left" : "right"
  }
  if (align === "center") {
    offX = metric.w / 2;
  } else if (align === "right") {
    offX = metric.w;
  }

  layer = view.appendChild(_doc.createElement("div"));
  _mix(layer.style, {
    font: me[FONT],
    position: "absolute",
    opacity: me[GLOBAL_ALPHA],
    height: _int(metric.h * 1.2) + "px",
    width: _int(metric.w * 1.2) + "px", // avoid word wrap
    left: (x - offX) + "px",
    top: y + "px"
  });

  if (sc[1]) {
    layer.style.textShadow = [me[SHADOWS[1]] + "px",
                              me[SHADOWS[2]] + "px",
                              me[SHADOWS[3]] + "px",
                              me[SHADOWS[0]]].join(" ");
  }
  name = wire ? STROKE_STYLE : FILL_STYLE;
  if (typeof me[name] === "string") {
    layer.style.color = me[name];
  }
  layer.textContent = text;
  canvas._canvasTextView.push(layer);
}

function fillTextSVG(me, text, x, y, maxWidth, wire) {
  text = text.replace(/(\t|\v|\f|\r\n|\r|\n)/g, " ");

  function svge(name) {
    return _doc.createElementNS("http://www.w3.org/2000/svg", name);
  }
  function attr(elm, hash) {
    for (var i in hash) {
      elm.setAttribute(i, hash[i]);
    }
  }
  function filter(svg, sx, sy, sb, sc) {
    var e = [];
    svg.appendChild(e[0] = svge("defs"));
      e[0].appendChild(e[1] = svge("filter"));
        e[1].appendChild(e[2] = svge("feGaussianBlur"));
        e[1].appendChild(e[3] = svge("feOffset"));
        e[1].appendChild(e[4] = svge("feFlood"));
        e[1].appendChild(e[5] = svge("feComposite"));
        e[1].appendChild(e[6] = svge("feMerge"));
          e[6].appendChild(e[7] = svge("feMergeNode"));
          e[6].appendChild(e[8] = svge("feMergeNode"));
    attr(e[1], {
      id:             "dropshadow",
      filterUnits:    "userSpaceOnUse"
    });
    attr(e[2], {
      "in":           "SourceAlpha",
      stdDeviation:   (sb < 8) ? sb / 2 : _math.sqrt(sb * 2)
    });
    attr(e[3], {
      dx:             sx,
      dy:             sy,
      result:         "offsetblur"
    });
    attr(e[4], {
      "flood-color":   sc[0],
      "flood-opacity": sc[1]
    });
    attr(e[5], {
      in2:            "offsetblur",
      operator:       "in"
    });
    attr(e[8], {
      "in":           "SourceGraphic"
    });
  }

  var style = wire ? me[STROKE_STYLE] : me[FILL_STYLE],
      types = (typeof style === "string") ? 0 : style._type,
      align = me[TEXT_ALIGN],
      dir = _runstyle(me.canvas, "").direction === "ltr",
      font = parseFont(me[FONT], me.canvas),
      metric = getTextMetric(text, me[FONT]),
      svg = svge("svg"),
      txt = svge("text"),
      sc = _colorCache[me[SHADOW_COLOR]] ||
           _parseColor(me[SHADOW_COLOR]),
      offset = { x: 0, y: 0 },
      margin = 100,
      validFontFamily;

  switch (align) {
  case "left":   align = "start"; break;
  case "center": align = "middle"; break;
  case "right":  align = "end"; break;
  case "start":  align = dir ? "start" : "end"; break;
  case "end":    align = dir ? "end"   : "start";
  }
  switch (align) {
  case "middle": offset.x = metric.w / 2; break;
  case "end":    offset.x = metric.w;
  }
  if (me[TEXT_BASELINE] === "top") {
    // text margin-top fine tuning
    offset.y = font.size /
        (FONT_SCALES[font.rawfamily.split(",")[0].toUpperCase()] ||
         me.xTextMarginTop);
  }
  attr(svg, {
    width:  metric.w + margin,
    height: metric.h + margin
  });
  if (sc[1]) {
    filter(svg, me[SHADOW_OFFSET_X], me[SHADOW_OFFSET_Y],
           me[SHADOW_BLUR], sc);
    attr(txt, {
      filter: "url(#dropshadow)"
    });
  }
  attr(txt, {
    x:              0 + margin / 2 + offset.x,
    y:              offset.y + offset.y / 2.4 + margin / 2,
    fill:           types ? me.xMissColor : style,
    "text-anchor":  align,
    "font-style":   font.style,
    "font-variant": font.variant,
    "font-size":    font.size + "px",
    "font-weight":  font.weight,
    "font-family":  font.family
  });
  validFontFamily = txt.getAttribute("font-family");
  if (!validFontFamily.replace(/[\"\']/g, "")) {
    return; // Opera9.5, Opera9.6 buggy
  }
  svg.appendChild(txt);
  txt.appendChild(_doc.createTextNode(text));

  _doc.body.appendChild(svg);
  me.save();
  me[GLOBAL_COMPO] = "source-over";
  try {
    me.drawImage(svg, x - margin / 2 - offset.x, y - margin / 2);
  } catch(err) {} // Opera9.2x
  me.restore();
  _doc.body.removeChild(svg);
}

// --- initialize ---
function initCanvas() {
  var lc = /loaded|complete/, rs = "readyState", fn,
      fnready ="onreadystatechange";

  if (!_ie) {
    if (_doc.getElementsByTagName("canvas").length) { // window.loaded state
      ++_canvasReady;
    } else if (_opera) {
      addEventListener("load", function() {
        ++_canvasReady;
      }, false);
    } else if (_webkit && _doc[rs]) {
      fn = function() {
        lc.test(_doc[rs]) ? ++_canvasReady : setTimeout(fn, 0);
      };
      fn();
    } else if (_gecko) {
      _doc.addEventListener("DOMContentLoaded", function() {
        ++_canvasReady;
      }, false);
    } else {
      ++_canvasReady;
    }
    return;
  }
  // --- IE part ---
  function initIE() {
    var v, node = _doc.getElementsByTagName("canvas"), i = node.length;
    while (i--) {
      v = node[i];
      _canvas.init(node[i],
          (!_slver || (" " + v.className + " ").indexOf(" vml ") >= 0));
    }
    ++_canvasReady;
  }

  if (lc.test(_doc[rs])) { // DOM already
    initIE();
  } else {
    fn = function() {
      lc.test(_doc[rs]) && (initIE(), _doc.detachEvent(fnready, fn));
    };
    _doc.attachEvent(fnready, fn);
  }
};

// --- export ---
_win.uuCanvas = _canvas; // window.uuCanvas
_canvas.SL2D = SL2D;
_canvas.VML2D = VML2D;
if (_ie) {
  _doc.createElement("canvas"); // dummy
  _win.CanvasRenderingContext2D = function() {};
  _win.CanvasGradient = Grad;
  _win.CanvasPattern = Patt;
}
initCanvas();

})(); // uuCanvas scope

// === uuLayer ===
// depend: uuMeta, uuCanvas, uuStyle, uuStyle.opacity, uuImage
/*

--- layer functions ---
new uuLayer(view, width = "auto", height = "auto")
uuLayer.getLayerInstance(elm) - return LayerObject or null

--- view operations ---
uuLayer.view
uuLayer.resizeView(width, height)
uuLayer.getViewInfo() - return { clid, cctx, front, rear,
                                 zmax, zmin, zorder, length }

--- layer operations ---
uuLayer.createLayer(id, type, hide = 0, back = 0,
                    width = void 0, height = void 0) - return new layer element
uuLayer.appendLayer(id, node, hide = 0) - return node
uuLayer.removeLayer(id) - return this
uuLayer.resizeLayer(id, width, height) - return this
uuLayer.refLayer(id) - return layer element
uuLayer.bringLayer(id, tgt) - return this
uuLayer.moveLayer(id = "", x, y, diff = false) - return this
uuLayer.showLayer(id = "") - return this
uuLayer.hideLayer(id = "") - return this
uuLayer.getLayerOpacity(id) - return 0.0 ~ 1.0
uuLayer.setLayerOpacity(id = "", opacity = 1.0, diff = false) - return this

  --- canvas 2D context operations ---
  uuLayer.getContext(id = "") - return canvas context or undefined
  uuLayer.push(id) - return this
  uuLayer.pop() - return this

    --- canvas 2D context style operations ---
    uuLayer.alphas(globalAlpha) - return this
    uuLayer.fills(fillStyle) - return this
    uuLayer.wires(strokeStyle, lineWidth = undef) - return this
    uuLayer.fonts(font) - return this
    uuLayer.lines(lineWidth) - return this
    uuLayer.shadows(color = undef,
                    x = undef, y = undef, blur = undef) - return this
    uuLayer.sets(propHash) - return this
    uuLayer.gets(propHash) - return { prop: value, ... }

    --- canvas 2D context drawing operations ---
    uuLayer.clear(x = 0, y = 0,
                  w = canvas.width, h = canvas.height) - return this
    uuLayer.save() - return this
    uuLayer.restore() - return this
    uuLayer.scale(x, y) - return this
    uuLayer.translate(x, y) - return this
    uuLayer.rotate(90 or "90deg" or "1.2rad") - return this
    uuLayer.transform(m11, m12, m21, m22, dx, dy) - return this
    uuLayer.begin(x = undef, y = undef) - return this
    uuLayer.move(x, y) - return this
    uuLayer.line(x, y) - return this
    uuLayer.curve(a0, a1, a2, a3,
                  a4 = undef, a5 = undef) - return this
    uuLayer.clip() - return this
    uuLayer.arc(x, y, r, a0 = "0deg",
                         a1 = "360deg", clock = 1) - return this
    uuLayer.draw(wire = 0) - return this
    uuLayer.close() - return this
    uuLayer.text(text, x = 0, y = 0,
                 wire = 0, maxWidth = undef) - return this
    uuLayer.measureText(text) - return { width, height }
    uuLayer.poly([point, ...], wire = 0) - return this
    uuLayer.box(x, y, w, h, r = 0, wire = 0) - return this
    uuLayer.boxpath(x, y, w, h, r = 0) - return this
    uuLayer.metabo(x, y, w, h, r = 0,
                   bulge = 10, wire = 0) - return this
    uuLayer.circle(x, y, w, h, r, wire = 0) - return this
    uuLayer.dots(x, y, w, h, {palette},
                 [data, ...], index = 0) - return this
    uuLayer.linearGrad(x1, y1,
                       x2, y2, [offset, ...],
                               [color, ...]) - return CanvasGradient
    uuLayer.radialGrad(x1, y1, r1,
                       x2, y2, r2, [offset, ...],
                                   [color, ...]) - return CanvasGradient
    uuLayer.pattern(image, pattern = "repeat") - return CanvasPattern
    uuLayer.image(image, arg1, arg2, arg3, arg4,
                         arg5, arg6, arg7, arg8) - return this

    --- canvas 2D context convenient operations ---

    uuLayer.fitImage(image) - return this
    uuLayer.grid(size = 10, unit = 5,
                 color = "skyblue", color2 = "steelblue") - return this
    uuLayer.angleGlossy(x, y, preset, extend) - return this
    uuLayer.metaboGlossy(x, y, preset, extend) - return this
    uuLayer.jellyBean(x, y, preset, extend) - return this
 */
(function() {
var _layer, // inner namespace
    _mm = uuMeta,
    _style = uuStyle,
    _image = uuImage,
    _doc = document,
    _ie = _mm.ie,
    _opera = _mm.opera,
    _int = parseInt,
    _float = parseFloat,
    _math = Math,
    _rad = _math.PI / 180, // Math.toRadians
//  _deg = 180 / _math.PI, // Math.toDegrees
    _runstyle = _mm.runstyle,
    _mix = _mm.mix,
    _instance = {}; // { uid: layer }

_layer =
    function(view,     // @param Node: layer container
             width,    // @param Number: view width
                       //        /CSSString(= "auto"): 300 "300px" "auto"
             height) { // @param Number: view height
                       //        /CSSString(= "auto"): 150 "150px" "auto"
  var uid = _mm.uid(view),
      vs = _ie ? view[_runstyle]
               : _runstyle(view, ""),
      w = (width  === void 0 || width  === "auto") ? "auto"
                                                   : _int(width) + "px",
      h = (height === void 0 || height === "auto") ? "auto"
                                                   : _int(height) + "px";

  if (uid in _instance) {
    return _instance[uid]; // already
  }

  // uuLayer.view - public property
  this.view = view;

  this._layer = {}; // Hash( { id: elm, ctx, chain } )
  // for canvas context
  this._stack = [];    // context stack
  this._cctx = void 0; // current canvas context
  this._clid = void 0; // current canvas context layer id

  _mix(view.style, {
    width: w,
    height: h,
    zIndex: _int(vs.zIndex) || 0,
    overflow: "hidden"
  });
  if (vs.position === "static") {
    view.style.position = "relative";
  }

  _instance[uid] = this;
};

// --- layer functions ---

// uuLayer.getLayerInstance
_layer.getLayerInstance = function(elm) { // @param Node:
                                          // @return LayerObject/null:
  var uid = _mm.uid(elm);
  if (uid in _instance) {
    return _instance[uid];
  }
  return null;
};


_layer.prototype = {

// --- view operations ---

  // uuLayer.resizeView - resize view
  resizeView: function(width,    // @param CSSPixelUnitString: "300px"
                       height) { // @param CSSPixelUnitString: "150px"
    this.view.style.width  = width;
    this.view.style.height = height;
  },

  // uuLayer.getViewInfo - get view state
  getViewInfo: function() { // @return Hash: { clid, cctx, front, rear,
                            //                 zmax, zmin, zorder, length }
    var hash = this._layer, v, i, j = 0,
        front, rear, max = 0, min = 0, order = [];

    for (i in hash) {
      ++j;
      v = _int(hash[i].elm.style.zIndex);
      (!front || max <= v) && (max = v, front = i);
      (!rear  || min >= v) && (min = v, rear = i);
      order[v] = i;
    }
    return { clid:  this._clid,   // current layer id
             cctx:  this._cctx,   // current canvas context
             front: front || "",  // front layer id
             rear:  rear  || "",  // rear layer id
             zmax:  max || 0,     // front layer z-index
             zmin:  min || 0,     // rear layer z-index
             zorder: order,       // order["a", "b", "c"]
             length: j };         // layer.length
  },

// --- layer operations ---

  // uuLayer.createLayer - create child layer
  createLayer:
      function(id,      // @param String: layer id
               type,    // @param String: "canvas", "vmlcanvas",
                        //                "div", "img", etc...
               hide,    // @param Boolean(= false): true = hidden layer
               back,    // @param Boolean(= false): back insertion
               width,   // @param Number(= undefined): canvas,image width
               height) {// @param Number(= undefined): canvas,image height
                        // @return Node: new layer element
    type = type.toLowerCase();

    var elm, es, ctx, v = this.view, viewInfo = this.getViewInfo();

    if (/canvas$/.test(type)) {
      elm = _doc.createElement("canvas");
      elm.width  = (width !== void 0) ? width :
                   (v.style.width === "auto") ? v.offsetWidth :
                   _int(v.style.width);
      elm.height = (height !== void 0) ? height :
                   (v.style.height === "auto") ? v.offsetHeight :
                   _int(v.style.height);
      elm = uuCanvas.init(elm, type === "vmlcanvas");
      if (_ie) {
        if (elm.unbind) {
          elm.unbind();
        }
      }
      es = elm.style;

      back ? v.insertBefore(elm, v.firstChild)
           : v.appendChild(elm);

      ctx = elm.getContext("2d");
      ctx.textBaseline = "top"; // force
      // set current context
      this._clid = id;
      this._cctx = ctx;
    } else {
      elm = _doc.createElement(type);
      es = elm.style;

      back ? v.insertBefore(elm, v.firstChild)
           : v.appendChild(elm);

      if (type !== "img") {
        es.width  = v.style.width;
        es.height = v.style.height;
      } else {
        (width  !== void 0) && (elm.width  = width);
        (height !== void 0) && (elm.height = height);
      }
    }
    es.zIndex = back ? (viewInfo.zmin - 1) : (viewInfo.zmax + 1);
    es.display = hide ? "none": "";
    es.position = "absolute";
    es.top = "0";
    es.left = "0";

    this._layer[id] = { elm: elm, ctx: ctx, chain: [] };
    return elm;
  },

  // uuLayer.appendLayer - add node
  appendLayer:
      function(id,      // @param String: layer id
               node,    // @param Node:
               hide) {  // @param Boolean(= false): true = hidden layer
                        // @return Node:
    var elm = node, type = elm.tagName.toLowerCase(), ctx;

    if (type === "canvas") {
      ctx = elm.getContext("2d");
      ctx.textBaseline = "top"; // force
      // set current context
      this._clid = id;
      this._cctx = ctx;
    }
    elm.style.zIndex = this.getViewInfo().length;

    this._layer[id] = { elm: elm, ctx: ctx, chain: [] };
    return elm;
  },

  // uuLayer.removeLayer - remove child layer
  removeLayer: function(id) { // @param String: layer id
                              // @return this:
    if (id in this._layer) {
      var v = this._layer[id], i = 0, iz = v.chain.length;
      for (; i < iz; ++i) {
        v.chain[i].elm.parentNode.removeChild(v.chain[i].elm);
        delete this._layer[v.chain[i]];
      }
      v.elm.parentNode.removeChild(v.elm);
      v.elm = void 0;
      delete this._layer[id];

      // reset context stack
      this._stack = [];
      this._clid = void 0;
      this._cctx = void 0;
    }
    return this;
  },

  // uuLayer.resizeLayer - resize child layer
  resizeLayer: function(id,       // @param String: layer id
                        width,    // @param Number: pixel width
                        height) { // @param Number: pixel height
                                  // @return this:
    var node = this._layer[id].elm;

    switch (node.tagName.toLowerCase()) {
    case "canvas":
    case "img":
      _ie && node.bind && node.bind();
      node.width  = width;
      node.height = height;
      _ie && node.unbind && node.unbind();
      break;
    default:
      node.style.width  = width  + "px";
      node.style.height = height + "px";
    }
    return this;
  },

  // uuLayer.refLayer - refer child layer
  refLayer: function(id) { // @param String: layer id
                           // @return Node: layer element
    return this._layer[id].elm;
  },

  // uuLayer.bringLayer - lift child layer
  //    bringLayer("a") is bring to front
  //    bringLayer("a", "b") is bring to layer("b")
  bringLayer: function(id,    // @param String: move layer id
                       tgt) { // @param String(= ""): target layer id
                              // @return this:
    var ly = this._layer, z, i,
        p1 = _int(ly[id].elm.style.zIndex),
        p2 = _int(ly[tgt || this.getViewInfo().front].elm.style.zIndex);

    if (p1 < p2) {
      for (i in ly) {
        z = _int(ly[i].elm.style.zIndex);
        if (z > p1 && z <= p2) {
          ly[i].elm.style.zIndex = z - 1;
        }
      }
      ly[id].elm.style.zIndex = p2;
    } else if (p1 === p2) {
      ly[id].elm.style.zIndex = p1 + 1;
    }
    return this;
  },

  _chains: function(id) {
    var rv = {}, v, i = 0, iz;

    rv[id] = v = this._layer[id];
    for (iz = v.chain.length; i < iz; ++i) {
      rv[v.chain[i]] = this._layer[v.chain[i]];
    }
    return rv;
  },

  // uuLayer.moveLayer - set absolute/relative child layer position
  //    moveLayer("", 100, 100) is move all layers to pos(100px, 100px)
  //    moveLayer("a", 100, 100, 1) is move layer("a") to pos(+100px, +100px)
  moveLayer:
      function(id,     // @param String(= ""): layer id
               x,      // @param Number: style.left value(unit px)
               y,      // @param Number: style.top value(unit px)
               diff) { // @param Boolean(= false): difference,
                       //           false = x and y is absolute value
                       //           true = x and y is relative value
                       // @return this:
    x = _int(x), y = _int(y), diff = diff || 0;

    var hash = id ? this._chains(id) : this._layer, v, i,
        doPixel = _ie || _opera;

    for (i in hash) {
      v = hash[i].elm.style;
      if (doPixel) {
        v.pixelLeft = (diff ? v.pixelLeft : 0) + x;
        v.pixelTop  = (diff ? v.pixelTop  : 0) + y;
      } else {
        v.left = (diff ? _int(v.left) : 0) + x + "px";
        v.top  = (diff ? _int(v.top)  : 0) + y + "px";
      }
    }
    return this;
  },

  // uuLayer.showLayer - show child layer
  //    showLayer() is show all layers
  //    showLayer("a") is show layer("a")
  showLayer: function(id) { // @param String(= ""): layer id
                            // @return this:
    return this._showLayer(id, 0);
  },

  // uuLayer.hideLayer - hide child layer
  //    hideLayer() is hide all layers
  //    hideLayer("a") is hide layer("a")
  hideLayer: function(id) { // @param String(= ""): layer id
                            // @return this:
    return this._showLayer(id, 1);
  },

  _showLayer: function(id, hide) {
    var hash = id ? this._chains(id) : this._layer, i;

    for (i in hash) {
      hash[i].elm.style.display = hide ? "none" : "block";
    }
    return this;
  },

  // uuLayer.getLayerOpacity - get child layer opacity value(from 0.0 to 1.0)
  getLayerOpacity:
      function(id) { // @param String: layer id
                     // @return Number: float value(min: 0.0, max: 1.0)
    return _style.getOpacity(this._layer[id].elm);
  },

  // uuLayer.setLayerOpacity - set child layer opacity value(from 0.0 to 1.0)
  //    setLayerOpacity("", 0.1, 1) is set all layers opacity(+0.1)
  //    setLayerOpacity("a", 0.5) is set layer("a") opacity(0.5)
  setLayerOpacity:
      function(id,      // @param String(= ""): layer id
               opacity, // @param Number(= 1.0): float value(0.0 to 1.0)
               diff) {  // @param Boolean(= false):
                        // @return this:
    opacity = (opacity === void 0) ? 1 : opacity;
    diff = diff || 0;

    var hash = id ? this._chains(id) : this._layer, i;

    for (i in hash) {
      _style.setOpacity(hash[i].elm, opacity, diff);
    }
    return this;
  },

// --- canvas 2D context operations ---

  // uuLayer.getContext - get canvas context
  getContext: function(id) { // @param String(= ""): layer id
                             //                      "" is current context
                             // @return CanvasRenderingContext2D/undefined:
    if (id) {
      return (id in this._layer) ? this._layer[id].ctx
                                 : void 0;
    }
    return this._cctx;
  },

  // uuLayer.push - push current context
  push: function(id) { // @param String: layer id
                       // @return this:
    this._clid && this._stack.push(this._clid);
    this._clid = id;
    this._cctx = this._layer[id].ctx;
    return this;
  },

  // uuLayer.pop - pop current context
  pop: function() { // @param String: layer id
                    // @return this:
    if (this._stack.length) {
      this._clid = this._stack.pop();
      this._cctx = this._layer[this._clid].ctx;
    }
    return this;
  },

// --- canvas 2D context style operations ---

  // uuLayer.alphas - set globalAlpha
  //    globalAlpha: from 0.0 to 1.0
  alphas: function(globalAlpha) { // @param Number: globalAlpha
                                  // @return this:
    this._cctx.globalAlpha = globalAlpha;
    return this;
  },

  // uuLayer.fills - set fillStyle
  fills: function(fillStyle) { // @param String/Object: fillStyle
                               // @return this:
    this._cctx.fillStyle = fillStyle;
    return this;
  },

  // uuLayer.wires - set strokeStyle
  wires:
      function(strokeStyle, // @param String/Object: strokeStyle
               lineWidth) { // @param Number(= undefined): lineWidth(from 1.0)
                            // @return this:
    this._cctx.strokeStyle = strokeStyle;
    if (lineWidth !== void 0) {
      this._cctx.lineWidth = lineWidth;
    }
    return this;
  },

  // uuLayer.fonts - set font style
  //    font: CSS font style value. (eg: "10px sans-serif")
  fonts: function(font) { // @param CSSFontString: font
                          // @return this:
    this._cctx.font = font;
    return this;
  },

  // uuLayer.lines - set lineWidth
  lines: function(lineWidth) { // @param Number: lineWidth(from 1.0)
                               // @return this:
    this._cctx.lineWidth = lineWidth;
    return this;
  },

  // uuLayer.setShadow - set shadow styles
  shadows: function(color,  // @param String(= undefined): shadowColor
                    x,      // @param Number(= undefined): shadowOffsetX
                    y,      // @param Number(= undefined): shadowOffsetY
                    blur) { // @param Number(= undefined): shadowBlur
                            // @return this:
    var ctx = this._cctx;
    (color !== void 0) && (ctx.shadowColor   = color);
    (x     !== void 0) && (ctx.shadowOffsetX = x);
    (y     !== void 0) && (ctx.shadowOffsetY = y);
    (blur  !== void 0) && (ctx.shadowBlur    = blur);
    return this;
  },

  // uuLayer.sets - set styles
  sets: function(propHash) { // @param Hash: { prop: value, ... }
                             // @return this:
    var ctx = this._cctx, i;
    for (i in propHash) {
      ctx[i] = propHash[i];
    }
    return this;
  },

  // uuLayer.gets - get styles
  gets: function(propHash) { // @param Hash: { font: "", ... }
                             // @return Hash: { font: "32pt Arial", ... }
    var ctx = this._cctx, i, rv = {};
    for (i in propHash) {
      rv[i] = ctx[i];
    }
    return rv;
  },

// --- canvas 2D context drawing operations ---

  // uuLayer.clear - clear rect
  clear: function(x,   // @param Number(= 0): position x
                  y,   // @param Number(= 0): position y
                  w,   // @param Number(= canvas.width):  width
                  h) { // @param Number(= canvas.height): height
                       // @return this:
    var ctx = this._cctx;
    ctx.clearRect(x || 0, y || 0,
                  w || ctx.canvas.width, h || ctx.canvas.height);
    return this;
  },

  // uuLayer.save
  save: function() { // @return this:
    this._cctx.save();
    return this;
  },

  // uuLayer.restore
  restore: function() { // @return this:
    this._cctx.restore();
    return this;
  },

  // uuLayer.scale - scale
  scale: function(w,   // @param Number: width scale
                  h) { // @param Number: height scale
                       // @return this:
    this._cctx.scale(w, h);
    return this;
  },

  // uuLayer.translate - offset origin
  translate: function(x,   // @param Number: offset x
                      y) { // @param Number: offset y
                           // @return this:
    this._cctx.translate(x, y);
    return this;
  },

  // uuLayer.rotate - rotate
  //    angle: 360 or "360deg" or "1.5rad"
  rotate: function(angle) { // @param Number/String: angle
                            // @return this:
    var ang = (/rad$/.test(angle + "") ? 1 : _rad) * _float(angle);
    this._cctx.rotate(ang);
    return this;
  },

  // uuLayer.transform
  transform: function(m11, m12, m21, m22, dx, dy) {
    this._cctx.transform(m11, m12, m21, m22, dx, dy);
    return this;
  },

  // uuLayer.begin - beginPath + moveTo
  begin: function(x,    // @param Number(= undefined): move x
                  y) {  // @param Number(= undefined): move y
                        // @return this:
    this._cctx.beginPath();
    (x !== void 0 && y !== void 0) && this._cctx.moveTo(x || 0, y || 0);
    return this;
  },

  // uuLayer.move - moveTo
  move: function(x,   // @param Number: move x
                 y) { // @param Number: move y
                      // @return this:
    this._cctx.moveTo(x, y);
    return this;
  },

  // uuLayer.line - lineTo
  line: function(x,   // @param Number: move x
                 y) { // @param Number: move y
                      // @return this:
    this._cctx.lineTo(x, y);
    return this;
  },

  // uuLayer.curve - quadraticCurveTo or bezierCurveTo
  curve: function(a0,   // @param Number:
                  a1,   // @param Number:
                  a2,   // @param Number:
                  a3,   // @param Number:
                  a4,   // @param Number(= undefined):
                  a5) { // @param Number(= undefined):
                        // @return this:
    if (a4 === void 0) {
      // cpx, cpy, x, y
      this._cctx.quadraticCurveTo(a0, a1, a2, a3);
    } else {
      // cp1x, cp1y, cp2x, cp2y, x, y
      this._cctx.bezierCurveTo(a0, a1, a2, a3, a4, a5);
    }
    return this;
  },

  // uuLayer.clip - clip
  clip: function() { // @return this:
    this._cctx.clip();
    return this;
  },

  // uuLayer.arc - arc
  //    a0 and a1: 360 or "360deg" or "0rad"
  arc: function(x,       // @param Number:
                y,       // @param Number:
                r,       // @param Number:
                a0,      // @param Number/String(= "0deg"): angle0
                a1,      // @param Number/String(= "360deg"): angle1
                clock) { // @param Boolean(= true):
                         // @return this:
    a0 = a0 || "0deg";
    a1 = a1 || "360deg";
    var ang0 = (/rad$/.test(a0 + "") ? 1 : _rad) * _float(a0),
        ang1 = (/rad$/.test(a1 + "") ? 1 : _rad) * _float(a1);
    this._cctx.arc(x, y, r, ang0, ang1,
                   (clock === void 0) ? 0 : !clock);
    return this;
  },

  // uuLayer.draw - fill or stroke
  draw: function(wire) { // @param Boolean(= false):
                         // @return this:
    wire ? this._cctx.stroke() : this._cctx.fill();
    return this;
  },

  // uuLayer.close - closePath
  close: function() { // @return this:
    this._cctx.closePath();
    return this;
  },

  // uuLayer.text
  text: function(text,       // @param String:
                 x,          // @param Number(= 0):
                 y,          // @param Number(= 0):
                 wire,       // @param Boolean(= false):
                 maxWidth) { // @param Number(= undefined):
                             // @return this:
    var fn = wire ? "strokeText" : "fillText";
    if (maxWidth === void 0) { // for Firefox3.5 bug
      this._cctx[fn](text, x || 0, y || 0);
    } else {
      this._cctx[fn](text, x || 0, y || 0, maxWidth);
    }
    return this;
  },

  // uuLayer.measureText - get text dimension
  measureText: function(text) { // @param String:
                                // @return TextMetrics: { width, height }
    this._cctx.measureText(text);
  },

  // uuLayer.poly - poly line + fill
  poly: function(point,  // @param PointArray: Array( [x0, y0, x1, y1, ... ] )
                 wire) { // @param Boolean(= false):
                         // @return this:
    var p = point || [0, 0], i, iz = point.length;
    this.close().begin(p[0], p[1]);
    for (i = 2; i < iz; i += 2) {
      this.line(p[i], p[i + 1]);
    }
    this.draw(wire).close();
  },

  // uuLayer.box - add box path, fill inside
  box: function(x,      // @param Number:
                y,      // @param Number:
                w,      // @param Number:
                h,      // @param Number:
                r,      // @param Number(= 0):
                wire) { // @param Boolean(= false):
                        // @return this:
    return this.boxpath(x, y, w, h, r).draw(wire);
  },

  // uuLayer.boxpath - add box path
  boxpath: function(x,   // @param Number:
                    y,   // @param Number:
                    w,   // @param Number:
                    h,   // @param Number:
                    r) { // @param Number(= 0):
                         // @return this:
    if (!r) {
      this._cctx.rect(x, y, w, h);
      return this;
    }
    if (r < 0) {
      r = 0;
    }
    // round corner
    return this.close().begin(x, y + r).line(x, y + h - r).
                curve(x, y + h, x + r, y + h).line(x + w - r, y + h).
                curve(x + w, y + h, x + w, y + h - r).line(x + w, y + r).
                curve(x + w, y, x + w - r, y).line(x + r, y).
                curve(x, y, x, y + r).
                close();
  },

  // uuLayer.metabolic - metabolic box
  metabo: function(x,      // @param Number:
                   y,      // @param Number:
                   w,      // @param Number:
                   h,      // @param Number:
                   r,      // @param Number(= 0):
                   bulge,  // @param Number(= 10):
                   wire) { // @param Boolean(= false):
                           // @return this:
    r = r || 0;
    bulge = (bulge === void 0) ? 10 : bulge;

    if (bulge) {
      return this.close().begin(x, y + r).line(x, y + h - r). // 1
                  curve(x + w * 0.5, y + h + bulge, x + w, y + h - r). // 2,3,4
                  line(x + w, y + r). // 5
                  curve(x + w, y, x + w - r, y).line(x + r, y). // 6,7
                  curve(x, y, x, y + r).draw(wire). // 8
                  close();
    }
    return this.close().begin(x, y + r).line(x, y + h). // 1
                line(x + w, y + h). // 2,3,4
                line(x + w, y + r). // 5
                curve(x + w, y, x + w - r, y).line(x + r, y). // 6,7
                curve(x, y, x, y + r).draw(wire). // 8
                close();
  },

  // uuLayer.circle - circle + fill
  circle: function(x,      // @param Number:
                   y,      // @param Number:
                   w,      // @param Number:
                   h,      // @param Number:
                   r,      // @param Number:
                   wire) { // @param Boolean(= false):
                           // @return this:
    if (w === h) { // circle
      return this.close().begin(x, y).arc(x, y, r).
                  draw(wire).close();
    }
    // ellipse(oval) not impl.
    return this;
  },

  // uuLayer.dots - draw dot with palette
  //    palette: Hash( { paletteNo: "#ffffff" or { r,g,b,a }, ...} )
  //    data: [paletteNo, paletteNo, ...]
  dots: function(x,       // @param Number:
                 y,       // @param Number:
                 w,       // @param Number:
                 h,       // @param Number:
                 palette, // @param Hash: color palette,
                 data,    // @param Array: dot data
                 index) { // @param Number(= 0): start data index
                          // @return this:
    var ctx = this._cctx, i = 0, j = 0, p, v, idx = index || 0;
    for (; j < h; ++j) {
      for (i = 0; i < w; ++i) {
        v = data[idx + i + j * w];
        if (!(v in palette)) {
          continue;
        }
        p = palette[v];
        if (typeof p === "string") {
          ctx.fillStyle = p;
        } else if (p.a) { // skip alpha = 0
          ctx.fillStyle = "rgba(" + [p.r, p.g, p.b, p.a].join(",") + ")";
        }
        ctx.fillRect(x + i, y + j, 1, 1);
      }
    }
    return this;
  },

  // uuLayer.linearGrad - create linear gradient
  linearGrad:
      function(x1,      // @param Number:
               y1,      // @param Number:
               x2,      // @param Number:
               y2,      // @param Number:
               offset,  // @param Array: [offset, ...], offset from 0.0 to 1.0
               color) { // @param Number: [color, ...]
                        // @return CanvasGradient:
    var rv = this._cctx.createLinearGradient(x1, y1, x2, y2),
        i = 0, iz = offset.length;

    for (; i < iz; ++i) {
      rv.addColorStop(offset[i], color[i]);
    }
    return rv; // CanvasGradient object
  },

  // uuLayer.radialGrad - create radial gradient
  radialGrad:
      function(x1,      // @param Number:
               y1,      // @param Number:
               r1,      // @param Number:
               x2,      // @param Number:
               y2,      // @param Number:
               r2,      // @param Number:
               offset,  // @param Array: [offset, ...], offset from 0.0 to 1.0
               color) { // @param Number: [color, ...]
                        // @return CanvasGradient:
    var rv = this._cctx.createRadialGradient(x1, y1, r1, x2, y2, r2),
        i = 0, iz = offset.length;

    for (; i < iz; ++i) {
      rv.addColorStop(offset[i], color[i]);
    }
    return rv; // CanvasGradient object
  },

  // uuLayer.pattern - create pattern
  pattern: function(image,     // @param HTMLImageElement
                               //        /HTMLCanvasElement:
                    pattern) { // @param String(= "repeat"):
                               // @return CanvasPattern:
    return this._cctx.createPattern(image, pattern || "repeat");
  },

  // uuLayer.image - image
  image: function(image,  // @param HTMLImageElement
                          //        /HTMLCanvasElement:
                  arg1,   // @param Number(= undefined):
                  arg2,   // @param Number(= undefined):
                  arg3,   // @param Number(= undefined):
                  arg4,   // @param Number(= undefined):
                  arg5,   // @param Number(= undefined):
                  arg6,   // @param Number(= undefined):
                  arg7,   // @param Number(= undefined):
                  arg8) { // @param Number(= undefined):
                          // @return this:
    switch (arguments.length) {
    case 1: this._cctx.drawImage(image, 0, 0); break;
    case 3: this._cctx.drawImage(image, arg1, arg2); break;
    case 5: this._cctx.drawImage(image, arg1, arg2, arg3, arg4); break;
    case 9: this._cctx.drawImage(image, arg1, arg2, arg3, arg4,
                                        arg5, arg6, arg7, arg8); break;
    default: throw "";
    }
    return this;
  },

// --- canvas 2D context convenient operations ---

  // uuLayer.fitImage - image fitting(auto-scaling)
  fitImage: function(image) { // @param HTMLImageElement: image element
                              // @return this:
    var ctx = this._cctx,
        dim = _image.getActualDimension(image),
        cw = _int(ctx.canvas.width),
        ch = _int(ctx.canvas.height),
        sw = dim.w,
        sh = dim.h,
        dx = (sw <= cw) ? _math.floor((cw - sw) / 2) : 0,
        dy = (sh <= ch) ? _math.floor((ch - sh) / 2) : 0,
        dw = (sw <= cw) ? sw : cw,
        dh = (sh <= ch) ? sh : ch;
    ctx.drawImage(image, 0, 0, sw, sh, dx, dy, dw, dh);
    return this;
  },

  // uuLayer.grid - draw hatch
  grid: function(size,     // @param Number(= 10):
                 unit,     // @param Number(= 5):
                 color,    // @param String(= "skyblue"):
                 color2) { // @param String(= "steelblue"):
                           // @return this:
    size = size || 10, unit = unit || 5;
    color = color || "skyblue", color2 = color2 || "steelblue";
    var x = size, y = size, i = 1, j = 1,
        w = _int(this._cctx.canvas.width),
        h = _int(this._cctx.canvas.height);

    for (; x < w; ++i, x += size) {
      this.wires((i % unit) ? color : color2).
           begin(x, 0).line(x, h).draw(1).close();
    }
    for (; y < h; ++j, y += size) {
      this.wires((j % unit) ? color : color2).
           begin(0, y).line(w, y).draw(1).close();
    }
    return this;
  },

  // uuLayer.angleGlossy
  //    override: Hash ( { gcolor1, gcolor2, overlayAlpha, w, h, r, angle } )
  angleGlossy: function(x,        // @param Number: move x
                        y,        // @param Number: move y
                        preset,   // @param String(= "GBLACK"): preset name
                        extend) { // @param Hash(= undefined): extend style
                                  // @return this:
    preset = (preset || "GBLACK").toUpperCase();
    preset = (preset in this.preset) ? this.preset[preset] : {};
    extend = _mix({ w: 100, h: 100, r: 12, angle: 0 }, preset, extend || {});

    var w = extend.w, h = extend.h, r = extend.r, angle = extend.angle,
        oa = extend.overlayAlpha, b = 3, dist = 0; // bevel size

    if (angle < -45) { angle = -45; }
    if (angle >  45) { angle =  45; }

    this.fills(this.linearGrad(x, y, x, y + h,
                               [0.0, 1.0], [extend.gcolor1, extend.gcolor2])).
         begin().box(x, y, w, h, r).close().
         fills("rgba(255,255,255," + oa + ")");

    switch (angle) {
    case 45:  this.begin(x + b, y + b + r).line(x + b, y + h - b * 2).
                    line(x + w - b * 2, y + b).line(x + b + r, y + b).
                    curve(x, y, x + b, y + b + r).draw().close(); break;
    case -45: this.begin(x - b + w, y + b + r).line(x - b + w, y + h - b * 2).
                    line(x + b * 2, y + b).line(x - b - r + w, y + b).
                    curve(x + w, y, x - b + w, y + b + r).draw().close(); break;
    default:  dist = ((h - b * 2) / 45 * angle) / 2;
              this.begin(x + b, y + b + r).
                    line(x + b, y + (h / 2) - b * 2 + dist).
                    line(x + w - b, y + (h / 2) - b * 2 - dist).
                    line(x + w - b, y + b + r).
                    curve(x + w, y, x + w - r, y + b).line(x + b + r, y + b).
                    curve(x, y, x + b, y + b + r).draw().close();
    }
    return this;
  },

  // uuLayer.metaboGlossy
  metaboGlossy: function(x,        // @param Number: move x
                         y,        // @param Number: move y
                         preset,   // @param String(= "GBLACK"): preset name
                         extend) { // @param Hash(= undefined): extend style
                                   // @return this:
    preset = (preset || "GBLACK").toUpperCase();
    preset = (preset in this.preset) ? this.preset[preset] : {};
    extend = _mix({ w: 100, h: 50, r: 12, bulge: 6 }, preset, extend || {});

    var w = extend.w, h = extend.h, r = extend.r, bulge = extend.bulge,
        oa = extend.overlayAlpha, r2 = r > 4 ? r - 4 : 0, b = 3; // bevel size

    this.fills(this.linearGrad(x, y, x, y + h,
                               [0.0, 1.0], [extend.gcolor1, extend.gcolor2])).
          begin().box(x, y, w, h, r).close().
          fills("rgba(255,255,255," + oa + ")").
          begin().metabo(x + b, y + b, w - b * 2, h * 0.5, r2, bulge).close();
    return this;
  },

  // uuLayer.jellyBean
  jellyBean: function(x,        // @param Number: move x
                      y,        // @param Number: move y
                      preset,   // @param String(= "GBLACK"): preset name
                      extend) { // @param Hash(= undefined): extend style
                                // @return this:
    extend = _mix({ w: 100, h: 30, r: 16, bulge: 6 }, extend || {});
    this.metaboGlossy(x, y, preset, extend);
    return this;
  },

// --- ---
  createReflectionLayer:
      function(id,             // @param String: layer id
               image,          // @param Node: image element
               hide,           // @param Boolean(= false): true = hidden layer
               x,              // @param Number(= 0): x
               y,              // @param Number(= 0): y
               width,          // @param Number(= image width): width
               height,         // @param Number(= image height): height
               mirrorHeight,   // @param Number(= 0.625): mirror height(0 to 1)
               offsetX,        // @param Number(= 0): offset X
               offsetY) {      // @param Number(= 0): offset Y
                               // @return Node: new layer element
    var dim = _image.getActualDimension(image),
        w = width || dim.w,
        h = height || dim.h,
        ox = offsetX || 0,
        oy = offsetY || 0,
        mh = (mirrorHeight === void 0) ? 0.625 : mirrorHeight;

    if (!_ie) {
      return this._addReflection(id, image, hide,
                                 dim, x || 0, y || 0, w, h, mh, ox, oy);
    }
    return this._addReflectionIE(id, image, hide,
                                 dim, x || 0, y || 0, w, h, mh, ox, oy);
  },

  _addReflection: function(id, image, hide, dim, x, y, w, h, mh, ox, oy) {
    var elm = this.createLayer(id, "canvas", hide),
        grad,
        sx = w / dim.w, // scale x
        sy = h / dim.h; // scale y

    elm.width  = w;
    elm.height = h + h * mh;

    this.moveLayer(id, x, y);

    x = 0, y = 0;
    grad = this.linearGrad(x, y + h, x, y + h + h * mh,
                           [mh, 0],
                           ["rgba(255, 255, 255, 1.0)",
                            "rgba(255, 255, 255, 0.3)"]),
    this.clear().
      save().translate(x, y).scale(sx, sy).image(image).restore().
      save().translate(x + ox, y + h * 2 + oy).
             scale(sx, -sy).image(image).restore().
      save().sets({ globalCompositeOperation: "destination-out" }).
             fills(grad).box(x, y + h, w, h * mh).restore();
    return elm;
  },

  _addReflectionIE: function(id, image, hide, dim, x, y, w, h, mh, ox, oy) {
    var DXIMG = "DXImageTransform.Microsoft.",
        BASIC = DXIMG + "BasicImage",
        ALPHA = DXIMG + "Alpha",
        img1 = this.createLayer(id, "img", hide),
        img2 = this.createLayer(id + "_reflection", "img", hide), // mirror
        ns = img2.style, obj;

    _mix(img1, { src: image.src, width: w, height: h });
    _mix(img2, { src: image.src, width: w, height: h });

    _mix(img1.style, { top: x + "px", left: y + "px" });
    _mix(img2.style, { top: (x + h + oy) + "px", left: (y + ox) + "px" });

    (ns.filter.indexOf(BASIC) < 0) && (ns.filter += " progid:" + BASIC);
    (ns.filter.indexOf(ALPHA) < 0) && (ns.filter += " progid:" + ALPHA);

    obj = img2.filters.item(BASIC);
    obj.Mask = 0;
    obj.Xray = 0;
    obj.Invert = 0;
    obj.Mirror = 1;
    obj.Opacity = 1;
    obj.Rotation = 2;
    obj.GrayScale = 0;
    obj.Enabled = 1;

    obj = img2.filters.item(ALPHA);
    obj.Style = 1;
    obj.StartX = 0;
    obj.StartY = 0;
    obj.FinishX = 0;
    obj.FinishY = (1 - mh) * 100;
    obj.Opacity = 70;
    obj.FinishOpacity = 0;
    obj.Enabled = 1;

    this._layer[id].chain.push(id + "_reflection");
    return img1;
  }
};

// --- initialize ---
(function() {
  function make(gcolo1, gcolor2, overlayAlpha) {
    return { gcolor1: gcolo1, gcolor2: gcolor2, overlayAlpha: overlayAlpha };
  }
  _layer.prototype.preset = {
    GBLACK:   make("#000",    "#333",    0.25),
    GGRAY:    make("black",   "silver",  0.38),
    GSLIVER:  make("gray",    "white",   0.38),
    GBLUE:    make("#0000a0", "#0097ff", 0.38),
    GGREEN:   make("#006400", "#00ff00", 0.38),
    GRED:     make("#400000", "#ff0000", 0.38),
    GLEMON:   make("#dfcc00", "#FFE900", 0.38),
    GGOLD:    make("#fffacd", "gold",    0.45), // lemonchiffon
    GPEACH:   make("violet",  "red",     0.38),
    GBLOODORANGE:
              make("orange",  "red",     0.38)
  };
})();

// --- export ---
window.uuLayer = _layer; // window.uuLayer

})(); // uuLayer scope

// === uuCSSValidator ===
// depend: uuColor, uuToken
/*
uuCSSValidator.width(value, rv = void 0) - return { width, valid }
uuCSSValidator.border(value, rv = void 0) - return { width, style, color }
uuCSSValidator.background(value, rv = void 0)
                              - return { image, repeat, position, attachment,
                                         origin, clip, rgba, valid }
uuCSSValidator.shadow(value, rv = void 0)
                              - return { rgba, ox, oy, blur, valid }
uuCSSValidator.gradient(value, rv = void 0)
                              - return { type, point, radius,
                                         offset, color, valid }
uuCSSValidator.borderRadius(value, rv = void 0)
                              - return { tl, tr, br, bl, valid }
 */
(function() {
var _validator, // inner namespace
    _color = uuColor,
    _token = uuToken,
    _float = parseFloat,
    TRIM = /^\s+|\s+$/g,
    LENGTH = /^(?:[\d\.]+(%|px|em|pt|cm|mm|in|pc|px)|0)$/;

_validator = {
  // uuCSSValidator.width - parse width: property
  width: function(value, // @param String: width value
                  rv) {  // @param Hash(= undefined): result value
                         // @return Hash: {
                         //            width: "value",
                         //            valid: 1 }
    rv = (rv === void 0) ? {} : rv;
    rv.width = value;
    rv.valid = (LENGTH.test(value) || /^auto$/.test(value)) ? 1 : 0;
    return rv;
  },

  // uuCSSValidator.border - parse border: short hand property
  border: function(value, // @param String: border value
                   rv) {  // @param Hash(= undefined): result value
                          // @return Hash: {
                          //            width: "value",
                          //            style: "value",
                          //            rgba:  "value",
                          //            valid: 1 }
    rv = (rv === void 0) ? {} : rv;

    var STY = /^(?:none|dotted|dashed|solid|double|groove|ridge|inset|outset)$/,
        ary = _token.split(value, " "), v, i = 0,
        width, style, rgba, r, valid = 1;

    while (valid && (v = ary[i++])) {
      if (LENGTH.test(v) || /^(?:thin|medium|thick)$/.test(v)) {
        width = v;
        continue;
      }
      if (STY.test(v)) {
        style = v;
        continue;
      }
      r = _color.parse(v, 1);
      if (r.valid) {
        rgba = r;
        continue;
      }
      valid = 0;
      break;
    }
    rv.width = width || "medium";
    rv.style = style || "none";
    rv.rgba  = rgba;
    rv.valid = valid;
    return rv;
  },

  // uuCSSValidator.background - parse background: short hand property
  //    background("url(...) top left, blue url(...) bottom center")
  background: function(value, // @param String: -uu-background: value
                       rv) {  // @param Hash(= undefined): result value
                              // @return Hash: {
                              //            image: ["url(...), ...],
                              //            repeat: ["repeat", ...],
                              //            position: ["0% 0%", ...],
                              //            attachment: ["scroll", ...],
                              //            origin: ["padding", ...],
                              //            clip: ["no-clip", ...],
                              //            rgba: { r,g,b,a },
                              //            valid: 1 }
    var BACKGROUND_POS = /^([\d\.]+(%|px|em|pt|cm|mm|in|pc|px)|left|center|right|top|bottom|0)$/,
        BACKGROUND_IDENT = /^(?:(-uu-gradient\(.*?\))|(none|url\(.*?\))|(repeat|no-repeat|repeat-x|repeat-y|space|round)|(scroll|fixed|local)|(border-box|padding-box|content-box)|(no-clip))$/,
        multi = _token.split(value.replace(/\s+/g, " "), ","),
        i = 0, j, iz = multi.length, jz, m, v, w, ary,
        img, rpt, att, pox, poy, ori, clp, rgba,
        valid = 1;

    if (rv === void 0) {
      rv = {
        image: [], repeat: [], position: [], attachment: [], 
        origin: [], clip: []
      };
    }

    while (valid && (v = multi[i++])) {
      img = rpt = pox = poy = ori = clp = "";
      ary = _token.split(v.replace(TRIM, ""), " ");

      for (j = 0, jz = ary.length; j < jz; ++j) {
        w = ary[j];
        if ( (m = BACKGROUND_IDENT.exec(w)) ) {
          if (m[1]) { img ? (valid = 0) : (img = m[1]); } // -uu-gradient
          if (m[2]) { img ? (valid = 0) : (img = m[2]); } // url
          if (m[3]) { rpt ? (valid = 0) : (rpt = m[3]); } // repeat
          if (m[4]) { att ? (valid = 0) : (att = m[4]); } // attachment
          if (m[5]) { ori ? (valid = 0) : (ori = m[5]); } // origin
          if (m[6]) { clp ? (valid = 0) : (clp = m[6]); } // clip
          continue;
        } else if ( (m = BACKGROUND_POS.exec(w)) ) {
          poy ? (valid = 0)
              : pox ? (poy = m[1])
                    : (pox = m[1]);
          continue;
        } else if (i === iz) {
          // color is permitted in <final-bg-layer>, but not in <bg-layer>
          // http://www.w3.org/TR/css3-background/
          if (!rgba) {
            rgba = _color.parse(w, 1);
            valid = rgba.valid;
            continue;
          }
        }
        // unknown token
        valid = 0;
        break;
      }
      rv.image.push(img || "none");
      rv.repeat.push(rpt || "repeat");
      rv.attachment.push(att || "scroll");
      rv.position.push((pox || "0%") + " " + (poy || "0%"));
      rv.origin.push(ori || "padding-box");
      rv.clip.push(clp || "no-clip");
    }
    rv.rgba = rgba || { r: 0, g: 0, b: 0, a: 0 }; // color
    rv.valid = valid; // valid
    return rv;
  },

  // uuCSSValidator.shadow - parse shadow: property
  //    box-shadow: <color> || <offsetX> <offsetY> <blur>, ...
  //    text-shadow: <color> || <offsetX> <offsetY> <blur>, ...
  shadow: function(value, // @param String: -uu-box-shadow: value
                   rv) {  // @param Hash(= undefined): result value
                          // @return Hash: {
                          //            rgba:  [{r,g,b,a}, ...],
                          //            ox:    ["0px", ...],
                          //            oy:    ["0px", ...],
                          //            blur:  ["0px", ...],
                          //            valid: 1 }
    var multi = _token.split(value.replace(TRIM, ""), ","),
        ary, v, i = 0, c, rgba, ox, oy, blur, valid = 1;

    if (rv === void 0) {
      rv = { rgba: [], ox: [], oy: [], blur: [] };
    }

    while (valid && (v = multi[i++])) {
      ary = _token.split(v, " ");

      rgba = /^\d/.test(ary[0]) ? ary.pop() : ary.shift();
      ox    = ary.shift() || 0;
      oy    = ary.shift() || 0;
      blur  = ary.shift() || 0;

      c = _color.parse(rgba, 1);
      valid = c.valid;

      rv.rgba.push(c); // rgba
      rv.ox.push(ox);
      rv.oy.push(oy);
      rv.blur.push(blur);
    }
    !rv.rgba.length && (valid = 0);
    rv.valid = valid;
    return rv;
  },

  // uuCSSValidator.gradient - parse gradient() value
  //    gradient(<type>, <point> [, <radius>]?,
  //                     <point> [, <radius>]? [, <stop>]*)
  gradient: function(value, // @param String: -uu-gradient() value
                     rv) {  // @param Hash(= undefined): result value
                            // @return Hash: {
                            //            type:  "linear" or "radial",
                            //            point:  [],
                            //            radius: [],
                            //            offset: [],
                            //            color:  [],
                            //            valid:  1 }
    var GRAD_TYPE = /^\s*(linear|radial)$/,
        GRAD_POINT = /^\s*(?:(left)|(right)|([\d\.]+%?))\s+(?:(top)|(bottom)|([\d\.]+%?))$/,
        GRAD_RADIUS = /^\s*([\d\.]+)$/,
        GRAD_FROM = /^\s*from/,
        GRAD_TO = /^\s*to/,
        GRAD_STOP = /^\s*color-stop/,
        type = 0, point = [], radius = [], from = 0, to = 0,
        offset = [], color = [], valid = 0,
        m, mm, ary, tmpary, v, w, i = 0;

    if (rv === void 0) {
      rv = {
        type: "", point: [], radius: [], offset: [], color: []
      };
    }

    if ( (m = /^-uu-gradient\((.*)\)$/.exec(value)) ) {
      valid = 1;
      ary = _token.split(m[1], ",");

      while (valid && (v = ary[i++])) {
        if (GRAD_TYPE.test(v)) {
          type ? (valid = 0)
               : (type = (v === "linear") ? 1 : 2); // 1: linear, 2: radial
        } else if ( ( mm = GRAD_POINT.exec(v)) ) {
          if (point.length >= 4) {
            valid = 0;
          } else {
            point.push(mm[1] ? "0" : mm[2] ? "100%" : mm[3]);
            point.push(mm[4] ? "0" : mm[5] ? "100%" : mm[6]);
          }
        } else if ( ( mm = GRAD_RADIUS.exec(v)) ) {
          if (type !== 2 || radius.length >= 2) {
            valid = 0;
          } else {
            radius.push(_float(mm[1]));
          }
        } else if (GRAD_FROM.test(v)) {
          if (from) {
            valid = 0;
          } else {
            v = v.replace(/\s*from\(\s*/, "").replace(/\s*\)$/, "");
            offset.push(0);
            color.push(v);
            from = 1;
          }
        } else if (GRAD_TO.test(v)) {
          if (to) {
            valid = 0;
          } else {
            v = v.replace(/\s*to\(\s*/, "").replace(/\s*\)$/, "");
            offset.push(1);
            color.push(v);
            to = 1;
          }
        } else if (GRAD_STOP.test(v)) {
          v = v.replace(/\s*color-stop\(\s*/, "").replace(/\s*\)$/, "");
          tmpary = v.split(/\s*,\s*/);
          w = tmpary.shift();
          offset.push(/%$/.test(w) ? _float(w) / 100
                                   : _float(w)); // "90%" -> 0.9
          color.push(tmpary.join(","));
        } else {
          // unknown token
          valid = 0;
        }
      }
    }
    if (point.length !== 4 ||
        (type === 2 && radius.length !== 2) ||
        !offset.length) {
      valid = 0;
    }

    rv.valid  = valid;
    rv.type   = (type === 1) ? "linear" :
                (type === 2) ? "radial" : "";
    rv.point  = point;    // ["0", "100%", "20", "20%"]
    rv.radius = radius;   // [radius1, radius2]
    rv.offset = offset;   // [0, ...]
    rv.color  = color;    // ["#C0FFEE", ... ]
    return rv;
  },

  // uuCSSValidator.border-radius - parse border-radius: property
  //    border-radius: <radius>{1,4} [/ <radius>{1,4}]
  //    border-radius: top-left, top-right, bottom-right, bottom-left
  //    border-radius: top-left, top-right, [top-left], [top-right]
  borderRadius: function(value, // @param String: -uu-border-radius: value
                         rv) {  // @param Hash(= undefined): result value
                                // @return Hash: {
                                //            tl: ["0px", "0px"],
                                //            tr: ["0px", "0px"],
                                //            br: ["0px", "0px"],
                                //            bl: ["0px", "0px"],
                                //            valid: 1 }
    var multi = value.replace(TRIM, "").split(/\s*\/\s*/),
        ary, v, i = 0, valid = 1;

    if (rv === void 0) {
      rv = { tl: [], tr: [], br: [], bl: [] };
    }

    while (valid && (v = multi[i++])) {
      ary = v.split(/\s+/);

      switch (ary.length) {
      case 1: rv.tl.push(ary[0]);
              rv.tr.push(ary[0]);
              rv.br.push(ary[0]);
              rv.bl.push(ary[0]);
              break;
      case 2: rv.tl.push(ary[0]);
              rv.tr.push(ary[1]);
              rv.br.push(ary[0]);
              rv.bl.push(ary[1]);
              break;
      case 3: rv.tl.push(ary[0]);
              rv.tr.push(ary[1]);
              rv.br.push(ary[2]);
              rv.bl.push(ary[1]); // bottom-left = top-right
              break;
      case 4: rv.tl.push(ary[0]);
              rv.tr.push(ary[1]);
              rv.br.push(ary[2]);
              rv.bl.push(ary[3]);
              break;
      default:valid = 0;
      }
    }
    if (!rv.tl.length || rv.tl.length > 2) {
      valid = 0;
    }
    rv.valid = valid;
    return rv;
  },

  // uuCSSValidator.box-reflect - parse box-reflect: property
  //    box-reflect: <direction> [<offset>] [<mask-box-image>]
  //    <direction> ::= "above" / "below" / "left" / "right"
  //    <offset> ::= length
  //    <mask-box-image> ::= -uu-gradient() or url()
  boxReflect: function(value, // @param String: -uu-box-reflect: value
                       rv) {  // @param Hash(= undefined): result value
                              // @return Hash: {
                              //            dir:    "below",
                              //            offset: "0",
                              //            url:    "",
                              //            grad:   void 0,
                              //            valid:  1}
    var DIR = /^(above|below|left|right)$/,
        MASK = /^(?:(-uu-gradient\(.*?\))|(none|url\(.*?\)))$/,
        m, v, i = 0, ary = _token.split(value, ""),
        dir, off, url, grad, valid = 1;

    if (rv === void 0) {
      rv = {};
    }

    while ( valid && (v = ary[i++]) ) {
      if (DIR.test(v)) {
        dir ? (valid = 0) : (dir = v);
        continue;
      }
      if (LENGTH.test(v)) {
        off ? (valid = 0) : (off = v);
        continue;
      }
      if ( (m = MASK.exec(v)) ) {
        if (m[1]) { (grad || url) ? (valid = 0) : (grad = m[1]); } // gradient
        if (m[2]) { (grad || url) ? (valid = 0) : (url  = m[2]); } // url
        continue;
      }
      // unknown token
      valid = 0;
      break;
    }

    if (valid && grad) {
      grad = _validator.gradient(grad);
      if (!grad.valid || !grad.type) {
        valid = 0;
      }
    }
    rv.valid  = valid;
    rv.dir    = dir;
    rv.offset = off || "0";
    rv.url    = url || "";
    rv.grad   = grad;
    return rv;
  }
};

// --- initialize ---

// --- export ---
window.uuCSSValidator = _validator;

})();


// === uuAltCSS ===
// depend: uuMeta, uuMeta.cdsl, uuMeta.event.resize,
//         uuToken, uuColor, uuImage, [uuCodec],
//         uuStyle, uuStyle.opacity, uuStyle.shadow, uuStyle.normalize,
//         uuStyleSheet, uuQuery, uuCanvas, uuLayer,
//         uuCSSValidator,
//         uuAltCSS.calcspec, uuAltCSS.cleanup, uuAltCSS.imports
/*
window.UUALTCSS_FORCE_MARKUP = undefined; // 1: force markup, 0: markup off
window.UUALTCSS_ENABLE_MAXMIN = 0
window.UUALTCSS_DECODE_DATAURI = 0;
window.UUALTCSS_VALUE_VALIDATION = 0;
window.UUALTCSS_STRIP_INLINE_WIDTH = 0;

uuAltCSS(context = void 0, rebuild = 0, css = "")
uuAltCSS.getRuleset() - return ruleset array
uuAltCSS.getDeclaredValues(node) - return Hash/undefined
uuAltCSS.getDirtyCSS() - return "dirty css"
uuAltCSS.getExStyle(node, prop) - return "extend / computed-like style"
uuAltCSS.setExStyle(node, prop, value, redraw = 0)
uuAltCSS.redraw()
 */
(function() {
var _altcss, // inner namespace
    _plus, // inner namespace
    _win = window,
    _doc = document,
    _mm = uuMeta,
    _mix = _mm.mix,
    _ss = uuStyleSheet,
    _token = uuToken,
    _color = uuColor,
    _style = uuStyle,
    _query = uuQuery,
    _bfx, // lazy, uuAltCSS.boxeffect
    _float = parseFloat,
    _validator = uuCSSValidator,
    _toPixel = _style.toPixel,
    _uaver = _mm.uaver,
    _egver = _mm.enginever,
    _ie = _mm.ie,
    _ie6 = _ie && _uaver === 6,
    _ie7 = _ie && _uaver === 7,
    _ie67 = _ie6 || _ie7,
    _runstyle = _mm.runstyle,
    _canvas = _mm.canvas && !(_mm.opera && _uaver < 9.5),
    _codec = ((_win.UUALTCSS_DECODE_DATAURI || 0)
                && _win.uuCodecDataURI) ? uuCodecDataURI : 0,
    // CSS Value Validation
    _enableValidation = _win.UUALTCSS_VALUE_VALIDATION || 0,
    // IE6, IE7: Size of an inline element is invalidated.
    //    <span style="width:100px; height:100px">inline with size</span>
    //          v
    //    <span style="width:auto; height:auto">inline without size</span>
    _stripWidth = _ie67 && (_win.UUALTCSS_STRIP_INLINE_WIDTH || 0),
    // IE6, IE7: Strip embed image
    //    <img src="data:image/*,...">
    //    <div style="background: url(data:image/*,...)">
    //          v
    //    <img src="1dot.gif">
    //    <div style="background: url(1dot.gif)">
    _enableDocumentFragment = !(_mm.gecko && _egver <= 1.9), // exclude Fx2, Fx3
    _ruleset = [],
    _ruleuid = 0,   // unique rule id
    _rule = {}, // unique rule
    _ssid = "uuAltCSS", // StyleSheet ID
    _root, // root node( <html> )
    _initialized = 0,
    _markup = 0,
    _boostup = 0, // opacity, position:fixed
    _specs = [], // raw data
    _data = {}, // raw data
    _lazyClearClass = [], // lazy clear className nodeList
    _uid2data = {}, // { uid: { node, rules, klass, excss }, ... }
                    // excss: { bits: 0, decl: {}, order: "" }
    _lastCSS = "",
    _deny = 0, // 1: deny removeChild
    _plan = { init: [], alphapng: [], boxeffect: [] },
    // CSS Validate for Acid2
    _validatorProps = {
      width: 1,
      border: 1,
      background: 1
    },
    _excss = { // extend css functions
/*
      hover:      _ie6    ? 0x8     : 0, // :hover
 */
      position:   _ie6    ? 0x10    : 0, // position: fixed, absolute bug
      alphapng:   _ie6    ? 0x20    : 0, // <img src="some.alpha.png">
      maxmin:     _ie67   ? 0x40    : 0, // max-width:
      disptbl:    _ie67   ? 0x80    : 0, // -uu-display: table
      opacity:    _ie     ? 0x100   : 0, // opacity:
      textshadow: _ie     ? 0x200   : 0, // -uu-text-shadow:
      boxeffect:  _canvas ? 0x400   : 0, // -uu-box-effect:
      boxshadow:  _canvas ? 0x800   : 0, // -uu-box-shadow:
      boxreflect: _canvas ? 0x1000  : 0, // -uu-box-reflect:
      bradius:    _canvas ? 0x2000  : 0, // -uu-border-radius:
      bimage:     _canvas ? 0x4000  : 0, // -uu-border-image:
      mbg:        _canvas ? 0x8000  : 0  // -uu-background:
                                         // -uu-background-color:
                                         // -uu-background-image:
                                         // -uu-background-repeat:
                                         // -uu-background-position:
    },
    _findExCSS = {
      decl: {
        position: 1,
        "-uu-display": 2,
        opacity:                          _excss.opacity,
        "-uu-text-shadow":                _excss.textshadow,
        "-uu-box-effect":                 _excss.boxeffect,
        "-uu-box-shadow":                 _excss.boxshadow,
        "-uu-box-reflect":                _excss.boxreflect,
        "-uu-border-image":               _excss.bimage,
        "-uu-border-radius":              _excss.bradius,
        "-uu-background":                 _excss.mbg,
        "-uu-background-color":           _excss.mbg,
        "-uu-background-image":           _excss.mbg,
        "-uu-background-repeat":          _excss.mbg,
        "-uu-background-position":        _excss.mbg
      },
      position:      { fixed: _excss.position },
      "-uu-display": { table: _excss.disptbl  }
    },
    BFX = "uuAltCSSBoxEffect",
    TRIM = /^\s+|\s+$/g,
    MEMENTO = "uuAltCSSMemento",
    DATA_SCHEME = /^data\:/,
    DETOXIFY_WIDTH = "width:auto;height:auto";

if (!_win.UUALTCSS_ENABLE_MAXMIN) {
  _excss.maxmin = 0;
}

// find excss keyword
function findExCSS(pair, expr) {
  var bits = 0, decl = {}, order = [], v, w, i = 0;

  while ( (v = pair[i++]) ) {
    switch (w = _findExCSS.decl[v.prop] || 0) {
    case 1: bits |= _findExCSS.position[v.val]; break; // position:
    case 2: bits |= _findExCSS["-uu-display"][v.val]; break; // -uu-display:
    default:
      bits |= w;
    }
    decl[v.prop] = v.val;
    order.push(v.prop);
  }
/*
  if (_ie6) {
    if (/:hover/.test(expr)) {
      bits |= _excss.hover;
    }
  }
 */
  return { bits: bits, decl: decl, order: order.join(",") + "," };
}

// uuAltCSS
_altcss =
    function(context, // @param Node/IDString(= void 0): revalidation context
             rebuild, // @param Number(= 0): 0 = OFF(quick), 1 = ON(full)
             css) {   // @param CSSString(= ""): CSS text

  // lazy revalidate for :target
  (_markup || _boostup) && setTimeout(function() {
    var ctx = (typeof context === "string") ? _query.id(context) : context,
        tick = +new Date;

    ctx = (!ctx || !ctx.parentNode || ctx === _doc) ? _doc
                                                    : ctx.parentNode;
    _altcss._unbond(ctx);
    if (rebuild) {
      _specs = [], _data = {}; // clear raw data
      _altcss._init(css);
    }
    _altcss._validate(ctx);
    _initialized = 1;

    _mm.debug && (_win.status = (new Date - tick) + "ms");

    !_mm.blackout &&
        (typeof _win.boot === "function") && _win.boot(0x201);
  }, 0);
};

_mm.mix(_altcss, {
  // uuAltCSS.getRuleset
  getRuleset: function() { // @return Array: rule-set
    return _ruleset;
  },

  // uuAltCSS.getDeclaredValues - get declaration values (raw CSS String)
  getDeclaredValues:
      function(node) { // @param Node:
                       // @return Hash/undefined: { bits, order, decl }
                       //             Number: bits, 0 to 0xffffffff
                       //             String: order, comma jointed string
                       //                            has,last-comma,
                       //             Hash: decl { color: "red", ... }
    var rv = _uid2data[_mm.uid(node)];

    return rv ? rv.excss : rv;
  },

  // uuAltCSS.getDirtyCSS - get last collected CSS
  getDirtyCSS: function() { // @return String: "dirty CSS"
    return _lastCSS;
  },

  // uuAltCSS.getExStyle - get extend style / computed-like style
  getExStyle:
      function(node,   // @param Node:
               prop) { // @param String: "color"
                       // @return String: "red"
    var rv = "", data, val, rs;

    if (/^-uu/.test(prop)) {
      if ( (data = _uid2data[_mm.uid(node)]) ) {
        if ( (val = data.excss.decl[prop]) ) {
          rv = val;
        }
      }
    } else {
      rs = _ie ? node[_runstyle]
               : _runstyle(node, "");
      rv = rs[_mm.normalize.style(prop)];
    }
    return rv;
  },

  // uuAltCSS.setExStyle - set extend style
  setExStyle: function(node,     // @param Node:
                       prop,     // @param String: "color"
                       value,    // @param String: "red"
                       redraw) { // @param Boolean(= false):
    var fn;

    if (value !== void 0) {
      if ( (fn = _altcss._setExStyle[prop]) ) {
        fn(node, prop, value);
        if (redraw) {
          _altcss.redraw();
        }
      } else {
        if (BFX in node) {
          /^margin/.test(prop) && (node[BFX].train.margin = 1);
          /^border/.test(prop) && (node[BFX].train.border = 1);
        }
        node.style[_mm.normalize.style(prop)] = value;
      }
    }
  },

  // unbond attrs
  _unbond: function(context) { // @param Node:
    var node, v, i, j;

    // remove class="uucss{n} ..."
    //   1. replace element.className
    //   2. remove element.uuCSSC attr
    _lazyClearClass = _query("[uuCSSC]", context);

    // remove "!important" style
    //   1. collect old style from element.uuCSSIHash
    //   2. remove element.uuCSSI attr
    if (!_ie) {
      node = _query("[uuCSSI]", context), i = 0;
      while ( (v = node[i++]) ) {
        for (j in v.uuCSSIHash) {
          v.style.removeProperty(j);
          v.style.setProperty(j, v.uuCSSIHash[j], "");
        }
        v.removeAttribute("uuCSSI"); // unmarkup
      }
    }
/*
    // remove uuCSSHover="uucsshover{n} ..."
    //   1. remove element.uuCSSHover attr
    node = _query("[uuCSSHover]", context), i = 0;
    while ( (v = node[i++]) ) {
      v.removeAttribute("uuCSSHover");
    }
 */
  },

  _init: function(css) {
    var node, v, i, hash, dstr;

    // memnto
    if (!_initialized && _ie) {
      node = _query.tag("style"), i = 0;
      while ( (v = node[i++]) ) {
        !(MEMENTO in v) && (v[MEMENTO] = v.innerHTML);
      }
    }

    // collect style sheets
    css = _altcss.cleanup(_lastCSS = (css || _altcss.imports()));

    // http://d.hatena.ne.jp/uupaa/20090619/1245348504
    if (!_initialized && _ie6) {
      v = _win.name;
      _win.name = ""; // clear
      if (/UNKNOWN[^\{]+?\{|:unknown[^\{]+?\{/.test(css) && // }}}}
          "UNKNOWN" !== v) {
        _win.name = "UNKNOWN";
        _mm.blackout = 1; // stop boot process
        location.reload(false);
        return false;
      }
    }

    // decode script
    //    <script src="data:text/javascript,..">
    if (_ie && _codec) {
      node = _query.tag("script"), i = 0;
      while ( (v = node[i++]) ) {
        if (DATA_SCHEME.test(v.src)) {
          hash = _codec.decode(v.src);
          if (hash.mime === "text/javascript") {
            dstr = String.fromCharCode.apply(null, hash.data);
            (new Function(dstr))();
          }
        }
      }
    }

    _altcss._parse(css);
    _specs.sort(function(a, b) { return a - b; });
    _ss.create(_ssid); // create stylesheet
    return true;
  },

  _validate: function(context) {
    // uuNode.cutdown - cut all nodes less than context
    function cutdown(context) { // @Node(= document.body): parent node
                                // @return DocumentFragment:
      var rv, ctx = context || _doc.body;
      if (_doc.createRange) {
        (rv = _doc.createRange()).selectNodeContents(ctx);
        return rv.extractContents(); // return DocumentFragment
      }
      rv = _doc.createDocumentFragment();
      while (ctx.firstChild) {
        rv.appendChild(ctx.removeChild(ctx.firstChild));
      }
      return rv;
    }

    var v, w, i = 0, j, k, l, iz = _specs.length, jz, kz, lz,
        spec, data, expr, ruleid, nodeuid, excss, node,
        IMP = " !important;",
/*
        skip = _ie6
             ? /^\*$|::?before$|::?after$|::?first-letter$|::?first-line$|:active|:focus|:unknown/
             : /^\*$|::?before$|::?after$|::?first-letter$|::?first-line$|:active|:focus|:unknown|:hover/,
        hover = /:hover/,
        pseudo,
 */
        skip = /^\*$|::?before$|::?after$|::?first-letter$|::?first-line$|:active|:focus|:hover|:unknown/,
        fragment, ruleset = [],
        // document fragment context
        dfctx = (!context || context === _doc ||
                             context === _root) ? _doc.body : context;

    _uid2data = {}; // reset data

    for (; i < iz; ++i) {
      spec = _specs[i];
      data = _data[spec];

      for (j = 0, jz = data.length; j < jz; ++j) {
        expr = data[j].expr;

        if (skip.test(expr)) { // skip universal, pseudo-class/elements
          continue;
        }

        try {
          if (_boostup) {
            excss = findExCSS(data[j].pair, expr);
          }
/*
          pseudo = 0;

          if (!_ie6 || !hover.test(expr)) {
            node = _query(expr, context);
          } else {
            node = _query(expr.replace(hover, function(m) {
              pseudo |= 0x2;
              return "";
            }), context);
          }
 */
          node = _query(expr, context);

          if (_ie || spec < 10000) {
            // make unique rule id from expr
            ruleid = _rule[expr] || (_rule[expr] = ++_ruleuid);

            // add new rule
            if (_markup) {
              // ".uucss[num] { color: red; font-size: 24pt; ... }"
              w = (spec < 10000) ? data[j].decl.join(";")
                                 : data[j].decl.join(IMP) + IMP;
              w += ";";
/*
              if (!pseudo) {
                ruleset.push(".uucss" + ruleid, w);
              } else if (pseudo & 0x2) { // 0x2: hover
                ruleset.push(".uucsshover" + ruleid, w);
              }
 */
              ruleset.push(".uucss" + ruleid, w);
            }
            for (k = 0, kz = node.length; k < kz; ++k) {
              v = node[k];
              nodeuid = _mm.uid(v); // node unique id

              // init container
              if (!(nodeuid in _uid2data)) {
                _uid2data[nodeuid] = {
                  node: v,
                  rules: {},
                  klass: [],
                  excss: { bits: 0, decl: {}, order: "" }
                };
              }

              // regist rule if not exists
              if (!(ruleid in _uid2data[nodeuid].rules)) {

                _uid2data[nodeuid].rules[ruleid] = ruleid;

                if (_markup) {
/*
                  if (!pseudo) {
                    _uid2data[nodeuid].klass.push("uucss" + ruleid);
                  } else if (pseudo & 0x2) { // 0x2: hover
                    if (!("uuCSSHover" in v)) {
                      v.uuCSSHover = ""; // bond
                    }
                    v.uuCSSHover += " uucsshover" + ruleid;
                  }
 */
                  // .uucss{n}
                  _uid2data[nodeuid].klass.push("uucss" + ruleid);
                }

                // for Acid2 test(need UUALTCSS_STRIP_INLINE_WIDTH = 1)
                //    inline-element has neither width nor height(in IE6, IE7)
                if (_stripWidth) {
                  if (/display:inline;/i.test(w) ||
                      /^inline$/.test(v.currentStyle.display)) {
/*
                    !pseudo &&
                        _markup && _uid2data[nodeuid].klass.push("uucssinline");
 */
                    _markup && _uid2data[nodeuid].klass.push("uucssinline");
                  }
                }
              }
              // mixin boostup info
              if (_boostup) {
                // mixin bits
                _uid2data[nodeuid].excss.bits |= excss.bits;

                // mixin declarations
                _mix(_uid2data[nodeuid].excss.decl, excss.decl);

                // append declaration order
                _uid2data[nodeuid].excss.order
                    += excss.order; // "has,last-comma,"
              }
            }
          } else { // "!important" route
            for (k = 0, kz = node.length; k < kz; ++k) {
              v = node[k];
              nodeuid = _mm.uid(v); // node unique id

              if (_markup) {
                v.setAttribute("uuCSSI", 1); // bond + markup for revalidate

                // init container
                !("uuCSSIHash" in v) && (v.uuCSSIHash = {}); // bond

                for (l = 0, lz = data[j].pair.length; l < lz; ++l) {
                  w = data[j].pair[l];
                  // save
                  v.uuCSSIHash[w.prop] = v.style.getPropertyValue(w.prop);
                  // overwrite
                  v.style.setProperty(w.prop, w.val, "important");
                }
              }

              // init container
              if (!(nodeuid in _uid2data)) {
                _uid2data[nodeuid] = {
                  node: v,
                  rules: {},
                  klass: [],
                  excss: { bits: 0, decl: {}, order: "" }
                };
              }
              // mixin boostup info
              if (_boostup) {
                // mixin bits
                _uid2data[nodeuid].excss.bits |= excss.bits;

                // mixin declarations
                _mix(_uid2data[nodeuid].excss.decl, excss.decl);

                // append declaration order
                _uid2data[nodeuid].excss.order
                    += excss.order; // "has,last-comma,"
              }
            }
          }
        } catch(err) {
          _mm.debug && 
              alert("validate fail: " + err);
        }
      }
    }

    _boostup && _plus.plan(_initialized, context);

    _enableDocumentFragment && !_deny && (fragment = cutdown(dfctx));
    // --- begin code block ---
        // lazy - clear all rules
        _ss.removeAllRules(_ssid);
        _ruleset = [];

        // lazy - clear all className
        for (i = 0, iz = _lazyClearClass.length; i < iz; ++i) {
          v = _lazyClearClass[i];
          if (v) {
            if (_ie67 && v.getAttribute("uuCSSLock")) {
              ;
            } else {
              v.className = v.className.replace(/uucss[\w]+\s*/g, "");
              v.removeAttribute("uuCSSC");
            }
          }
        }

        // apply to className
        if (_markup) {
          for (nodeuid in _uid2data) {
            v = _uid2data[nodeuid].node;
            if (_ie67 && v.getAttribute("uuCSSLock")) {
              ;
            } else {
              w = v.className + " " + _uid2data[nodeuid].klass.join(" ");
              v.className = w.replace(TRIM, "").replace(/\s+/g, " ");
              v.setAttribute("uuCSSC", "1"); // bond + markup for revalidate
            }
          }
        }
        // insert rule
        for (i = 0, iz = ruleset.length; i < iz; i += 2) {
          _ss.insertRule(_ssid, ruleset[i], ruleset[i + 1]);
          _ruleset.push(ruleset[i] + " { " + ruleset[i + 1] + " }");
        }
        // strip width
        if (_stripWidth) {
          _ss.insertRule(_ssid, ".uucssinline", DETOXIFY_WIDTH);
          _ruleset.push(".uucssinline" + " { " + DETOXIFY_WIDTH + " }");
        }
        // boost prevalidate
        if (_boostup) {
          _plus.prevalidate(_initialized, context);
        }
    // --- end code block ---
    _enableDocumentFragment && !_deny && dfctx.appendChild(fragment);

    // boost postvalidate
    if (_boostup) {
      _plus.postvalidate(_uid2data, _initialized, context);
    }

    // Opera9.5+ problem fix and Opera9.2 flicker fix
    if (_mm.opera) {
      _enableDocumentFragment = 0;
    }
  },

  _parse: function(css) {
    function esc(m, q, str) { // escape "{};,"
      ++escape;
      return q + str.replace(/\{/g, "\\u007B").replace(/;/g, "\\u003B").
                     replace(/\}/g, "\\u007D").replace(/,/g, "\\u002C") + q;
    }
    function unesc(str) { // unescape "{};,"
      return str.replace(/\\u007B/g, "{").replace(/\\u003B/g, ";").
                 replace(/\\u007D/g, "}").replace(/\\u002C/g, ",");
    }

    var escape = 0, v, i, j, k, iz, jz, kz,
        gd1, gd2, gp1, gp2, ary, expr, decl, decls, exprs, spec,
        rex1 = /\s*\!important\s*/,
        rex2 = /\s*\!important\s*/g,
        ignore, prop, val, valid, both;

    v = css.replace(/(["'])(.*?)\1/g, esc);
    if (_ie) {
      v = v.replace(/^\s*\{/,   "*{").  // }
            replace(/\}\s*\{/g, "}*{"). // }
            replace(/\{\}/g,    "{ }"); // for IE Array.split bug
    }
    ary = v.split(/\s*\{|\}\s*/);
    !_ie && ary.pop(); // for IE Array.split bug

    if (ary.length % 2) {
      return; // parse error
    }

    for (i = 0, iz = ary.length; i < iz; i += 2) {
      expr = ary[i]; // "E>F,G"
      decl = ary[i + 1].replace(TRIM, ""); // "color: red,text-aligh:left"
      exprs = (expr + ",").split(/\s*,\s*/);
      decls = (decl + ";").split(/\s*;\s*/);
      !_ie && (exprs.pop(), decls.pop()); // IE split bug

      gd1 = [], gd2 = [], gp1 = [], gp2 = [];
      for (k = 0, kz = decls.length; k < kz; ++k) {
        ignore = 0;

        if (decls[k]) {
          both = decls[k].split(/\s*:\s*/);
          prop = both.shift();  // "color"
          val  = escape ? unesc(both.join(":")): both.join(":"); // "red"

          if (/\\/.test(prop)) { // .parser { m\argin: 2em; };
            ++ignore;
          } else if (rex1.test(val)) { // !important
            val = val.replace(rex2, ""); // trim "!important"
            valid = (_enableValidation && _validatorProps[prop]) ?
                        _validator[prop](val).valid : 1;
            if (valid) {
              gd2.push(prop + ":" + val);
              gp2.push({ prop: prop, val: val });
            } else {
              ++ignore;
            }
          } else {
            valid = (_enableValidation && _validatorProps[prop]) ?
                        _validator[prop](val).valid : 1;
            if (valid) {
              gd1.push(prop + ":" + val); // "color:red"
              gp1.push({ prop: prop, val: val }); // { prop:"color", val:"red" }
            } else {
              ++ignore;
            }
          }
          ignore && _mm.debug &&
              alert('"' + prop + ":" + val + '" ignore decl');
        }
      }
      for (j = 0, jz = exprs.length; j < jz; ++j) {
        v = escape ? unesc(exprs[j]) : exprs[j];

        // * html .parser {  background: gray; }  -> "gray"
        if (/^\s*\*\s+html/i.test(v)) {
          _mm.debug && alert(v + " ignore CSS Star hack");
          continue; // ignore rule set
        }
        spec = _altcss.calcspec(v);
        if (gd1.length) {
          !(spec in _data) && (_specs.push(spec), _data[spec] = []);
          _data[spec].push({ expr: v, decl: gd1, pair: gp1 });
        }
        if (gd2.length) { // !important
          spec += 10000;
          !(spec in _data) && (_specs.push(spec), _data[spec] = []);
          _data[spec].push({ expr: v, decl: gd2, pair: gp2 });
        }
      }
    }
  },

  _setExStyle: {
    opacity: function(node, prop, value) {
      _style.setOpacity(node, value);
    },
    "-uu-text-shadow": function(node, prop, value) {
      var shadow = _validator.shadow(value);

      if (!shadow.valid) {
        throw prop + "=" + value;
      }
      _style.setTextShadow(node, shadow.rgba, shadow.ox,
                           shadow.oy, shadow.blur);
    },
    "-uu-box-effect": function(node, prop, value) {
      if (!/^(?:none|auto)$/.test(value)) { throw prop + "=" + value; }

      var data = _uid2data[_mm.uid(node)];

      data.excss.decl[prop] = value; // update

      !(BFX in node) && _bfx.bond(node, data.excss);
      node[BFX].boxeffect.render = { none: 0, auto: 1 }[value];

      // for box-reflection:
      if (node[BFX].hasReflectLayer) {
        if (!node[BFX].boxeffect.render) {
          node[BFX].layer.hideLayer("reflect");
          node.style.visibility = "";
        } else {
          node[BFX].layer.showLayer("reflect");
          node.style.visibility = "hidden";
        }
      }
    },
    "-uu-box-shadow": function(node, prop, value) {
      var rv = _validator.shadow(value), hash, data;

      if (!rv.valid) { throw prop + "=" + value; }
      data = _uid2data[_mm.uid(node)];
      data.excss.decl[prop] = value; // update

      !(BFX in node) && _bfx.bond(node, data.excss);
      hash = node[BFX].boxshadow;
      hash.render = 1;
      hash.rgba = rv.rgba[0];
      hash.ox   = _toPixel(node, rv.ox[0]);
      hash.oy   = _toPixel(node, rv.oy[0]);
      hash.blur = _toPixel(node, rv.blur[0]);
    },
    "-uu-box-reflect": function(node, prop, value) {
      var rv = _validator.boxReflect(value),
          data = _uid2data[_mm.uid(node)], hash;

      if (!rv.valid) { throw prop + "=" + value; }
      data.excss.decl[prop] = value; // update

      !(BFX in node) && _bfx.bond(node, data.excss);
      hash = node[BFX].boxreflect;
      hash.render = 1;
      hash.dir    = rv.dir;
      hash.offset = _toPixel(node, rv.offset);
    },
    "-uu-border-radius": function(node, prop, value) {
      var rv = _validator.borderRadius(value), hash, data;

      if (!rv.valid) { throw prop + "=" + value; }
      data = _uid2data[_mm.uid(node)];
      data.excss.decl[prop] = value; // update

      !(BFX in node) && _bfx.bond(node, data.excss);
      hash = node[BFX].bradius;

      if (rv.tl[0] || rv.tr[0] || rv.br[0] || rv.bl[0]) {
        hash.render = 1;
        hash.r = [_toPixel(node, rv.tl[0]), _toPixel(node, rv.tr[0]),
                  _toPixel(node, rv.br[0]), _toPixel(node, rv.bl[0])];
      }
      if (hash.r[0] === hash.r[1] && hash.r[1] === hash.r[2] &&
          hash.r[2] === hash.r[3]) {
        hash.shorthand = 1;
      }
    },
    "-uu-border-image": function(node, prop, value) {
      // ToDo:
    },
    "-uu-background": function(node, prop, value) {
      var rv = _validator.background(value),
          data = _uid2data[_mm.uid(node)];

      if (!rv.valid) { throw prop + "=" + value; }
      data.excss.decl[prop] = value; // update

      !(BFX in node) && _bfx.bond(node, data.excss);
      _mm.mix(node[BFX].mbg, rv);
      node[BFX].train.mbg = 1;
    },
    "-uu-background-color": function(node, prop, value) {
      var rv = _color.parse(value, 1), data = _uid2data[_mm.uid(node)];

      if (!rv.valid) { throw prop + "=" + value; }
      data.excss.decl[prop] = value; // update

      !(BFX in node) && _bfx.bond(node, data.excss);
      node[BFX].mbg.rgba = rv;
      node[BFX].train.mbg = 1;
    },
    "-uu-background-image": function(node, prop, value) {
      var rv = _token.split(value, ","), data = _uid2data[_mm.uid(node)];

      data.excss.decl[prop] = value; // update

      !(BFX in node) && _bfx.bond(node, data.excss);
      node[BFX].mbg.image = rv;
      node[BFX].train.mbg = 1;
    },
    "-uu-background-repeat": function(node, prop, value) {
      var rv = value.split(","), data = _uid2data[_mm.uid(node)];

      data.excss.decl[prop] = value; // update

      !(BFX in node) && _bfx.bond(node, data.excss);
      node[BFX].mbg.repeat = rv;
      node[BFX].train.mbg = 1;
    },
    "-uu-background-position": function(node, prop, value) {
      var rv = value.split(","), data = _uid2data[_mm.uid(node)];

      data.excss.decl[prop] = value; // update

      !(BFX in node) && _bfx.bond(node, data.excss);
      node[BFX].mbg.position = rv;
      node[BFX].train.mbg = 1;
    }
  }
});

//
_plus = {
  // plus.init
  init: function(context) {
    if (_excss.position) {
      _plan.init = _altcss.ie.position.init(context);
    }
  },

  // plus.plan - make plan
  plan: function(revalidate, context) {
    if (!revalidate) {
      if (_excss.alphapng) {
        _plan.alphapng = _altcss.ie.alphapng.query(context);
      }
    }
  },

  // plus.prevalidate - pre-validate plan
  prevalidate: function(revalidate, context) {
    var v, i = 0;

    if (!revalidate) {
      if (_plan.init.length) {
        while ( (v = _plan.init[i++]) ) {
          v();
        }
        _plan.init = []; // clear
      }
      if (_excss.alphapng) {
        _altcss.ie.alphapng(_plan.alphapng);
        _plan.alphapng = []; // clear
      }
    }
  },

  // plus.postvalidate - post-validate plan
  postvalidate: function(uid2data, revalidate, context) {
    var i = 0, uid, node, excss, bits, ns, disptbl = [], boxeffect = [];

    for (uid in uid2data) {
      node = uid2data[uid].node;
      excss = uid2data[uid].excss;
      bits = excss.bits;

      if (bits & _excss.opacity) { // for IE6-IE8
        ns = node.style.opacity || node.currentStyle.opacity;
        _style.setOpacity(node, _float(ns) || 1.0);
      }

      if (bits & _excss.textshadow) { // for IE6-IE8
        shadow = _validator.shadow(excss.decl["-uu-text-shadow"]);
        if (shadow.valid) {
          _style.setTextShadow(node, shadow.rgba, shadow.ox,
                                                  shadow.oy, shadow.blur);
        }
      }

      (bits & _excss.position) && _altcss.ie.position.markup(node);

      if (!revalidate) {
        if (bits & _excss.disptbl) {
          disptbl.push(node); // stock
        }
/*
        if (bits & _excss.hover) {
          (function(node) {
            _mm.event.bind(node, "mouseenter", function(evt) {
              node.className += node.uuCSSHover;
            });
            _mm.event.bind(node, "mouseleave", function(evt) {
              node.className =
                  node.className.replace(/\s*uucsshover[\d]+/g, "");
            });
          })(node);
        }
 */
        if (bits & (_excss.boxshadow  |
                    _excss.boxreflect |
                    _excss.bradius    |
                    _excss.bimage     |
                    _excss.mbg)) {
          boxeffect.push(node); // stock
        }
      }
    }
    if (!revalidate) {
      _excss.position && _altcss.ie.position();
      _excss.disptbl && _altcss.ie.disptbl(disptbl);
      if (_excss.boxeffect) {
        i = 0;
        while ( (node = boxeffect[i++]) ) {
          _altcss.boxeffect(node, _uid2data[_mm.uid(node)].excss);
        }
      }
    }
    _excss.maxmin && (_altcss.ie.maxmin.markup(context),
                      _altcss.ie.maxmin());

    if (!revalidate) {
      if (_excss.position | _excss.maxmin | boxeffect.length) {
        _mm.event.resize(_altcss.redraw, 1); // use resize-agent
      }
    }
  }
};

// --- initialize ---
function init() {
  var css = "", context, tick = +new Date;

  _root = _query.tag("html")[0];

  _bfx = _altcss.boxeffect;

  if (_markup || _boostup) {
    if (_altcss._init(css)) {
      _boostup && _plus.init(context);
      _altcss._validate(context);
      _initialized = 1;
    }
    _mm.debug && (_win.status = (new Date - tick) + "ms");
  }
  !_mm.blackout &&
      (typeof _win.boot === "function") && _win.boot(0x200);
}

_ie        && (_uaver >= 6)                    && (++_markup, ++_boostup);
_mm.opera  && (_uaver >= 9.2 && _uaver <  9.5) && ++_markup; // Opera9.2-9.5
_mm.opera  && (_uaver >= 9.2)                  && ++_boostup;
_mm.gecko  && (_egver >  1.8 && _egver <= 1.9) && ++_markup; // Fx2-Fx3
_mm.gecko  && (_egver >  1.8)                  && ++_boostup;
_mm.webkit && (_egver >= 522 && _egver <  530) && ++_markup; // Safari3+
_mm.webkit && (_egver >= 522)                  && ++_boostup;

switch (_win.UUALTCSS_FORCE_MARKUP || 2) {
case 0: _markup = 0; break;
case 1: _markup = 1;
}
_mm.cdsl && _mm.cdsl();
_mm.boot(init);

// --- export ---
_altcss.ie = {};
_altcss.deny = _deny;
_altcss.redraw = function() {
  _excss.boxeffect && _altcss.boxeffect.recalc();
  _excss.position && _altcss.ie.position();
  _excss.maxmin && _altcss.ie.maxmin();
};
_win.uuAltCSS = _altcss;

})(); // uuAltCSS scope


// === uuAltCSS.ie ===
// depend: uuMeta, uuMeta.data, uuToken, uuQuery, uuStyle, uuStyleSheet
(function() {
var _position,    // inner namespace
    _alphapng,    // inner namespace
    _disptbl,     // inner namespace
    _maxmin,      // inner namespace
    _doc = document,
    _float = parseFloat,
    _mm = uuMeta,
    _mix = _mm.mix,
    _style = uuStyle,
    _query = uuQuery,
    _uaver = _mm.uaver,
    _ie = _mm.ie,
    _ie6 = _ie && _uaver === 6,
    _ie7 = _ie && _uaver === 7,
    _ss = uuStyleSheet,
    _ssid = "uuAltCSSIE",
    _spacer = uuMeta.imagedir + "/1dot.gif",
    _job = { position: [], maxmin: [] },
    ALPHA1 = "progid:DXImageTransform.Microsoft.AlphaImageLoader",
    ALPHA2 = " " + ALPHA1 + "(src='#',sizingMethod='image')";


// === uuAltCSS.ie.position ===
// care: position: absolute bug(cannot select text) for IE6
// care: position: fixed for IE6
// care: smooth scroll for IE6
// care: background image cache for IE6

// uuAltCSS.ie.position - apply position
_position = function() {
  if (!_job.position.length) { return; }

  var ary = [], v, w, i = 0, iz = _job.position.length,
      vp = _style.getViewport(), cs, p;

  for (; i < iz; ++i) {
    v = _job.position[i];
    if (v && (p = v.uuCSSPosition)) {
      cs = v.currentStyle;
      w = _style.toPixel(v, p.vcss, 1);
      p.vpx = (p.mode & 0x1) ? (_style.getPixel(v, "paddingTop") + w)
                             : (vp.h - v.offsetHeight - w);
      w = _style.toPixel(v, p.hcss, 1);
      p.hpx = (p.mode & 0x4) ? (_style.getPixel(v, "paddingLeft") + w)
                             : (vp.w - v.offsetWidth - w);
      ary.push(v);
    }
  }
  // http://www.microsoft.com/japan/msdn/columns/dude/dude061198.aspx
  ary.length && _doc.recalc(); // update

  _job.position = ary;
};

_mix(_position, {
  // uuAltCSS.ie.position.init - init position
  init: function(context) { // @param Node: context
                            // @return NodeArray:
    function markup(elm) {
      rv.push(function() {
        var st = elm.style;
        // text selection bug
        if (!_mm.quirks) {
          st.height = "100%";
  //      st.margin  = "0"; // ToDo
  //      st.padding = "0"; // ToDo
        }
        st.backgroundAttachment = "fixed"; // smooth scroll
        !_style.getBGImg(elm) && (st.backgroundImage = "url(none)");
      });
    }
    var rv = [];

    markup(_query.tag("html")[0]); // <html>
    markup(_doc.body);             // <body>

    rv.push(function() {
      try {
        _doc.execCommand("BackgroundImageCache", false, true);
      } catch(err) {}
    });
    rv.push(function() {
      _ss.create(_ssid);
      _ss.insertRule(_ssid, ".uuposfix", // not "uucssposfix"
        ("z-index:5000;" +
         "behavior:expression(" +
         "this.style.pixelTop=document.#.scrollTop+this.uuCSSPosition.vpx," +
         "this.style.pixelLeft=document.#.scrollLeft+this.uuCSSPosition.hpx)").
        replace(/#/g, _mm.quirks ? "body" : "documentElement")
      );
    });
    return rv;
  },

  // uuAltCSS.ie.posfxd.markup - markup position fixed nodes
  markup: function(node) { // @param Node: context
    if ("uuCSSPosition" in node) { return; } // bonded

    var vp = _style.getViewport(), rect = _style.getRect(node),
        cs = node.currentStyle,
        v = cs.top  !== "auto", // vertical
        h = cs.left !== "auto", // horizontal
        pxfn = _style.getPixel;

    _job.position.push(node);

    node.uuCSSPosition = { // bond
      mode: (v ? 1 : 2) | (h ? 4 : 8), // 1:top, 2:bottom, 4:left, 8:right
      vcss: v ? cs.top : cs.bottom,
      hcss: h ? cs.left : cs.right,
      vpx: v ? (pxfn(node, "paddingTop") + pxfn(node, "top"))
             : (vp.h - rect.oh - pxfn(node, "bottom")),
      hpx: h ? (pxfn(node, "paddingLeft") + pxfn(node, "left"))
             : (vp.w - rect.ow - pxfn(node, "right"))
    };
    node.className += " uuposfix";
    node.style.position = "absolute"; // position:fixed -> position:absolute
  }
});

// === uuAltCSS.ie.alphapng ===
// care: alpha.png for IE6

// uuAltCSS.ie.alphapng - apply alpha png filter
_alphapng = function(ary) { // @param NodeArray:
  var v, i = 0, hash;

  while ( (v = ary[i++]) ) {
    hash = _mm.data(v, "uuAltCSS.ie.alphapng");
    if (!hash || !hash.efx) {
      hash.efx = 1;

      v.style.filter += ALPHA2.replace(/#/, v.src);
      v.src = _spacer;
      (v.tagName === "IMG") ? (v.width = hash.w, v.height = hash.h)
                            : (v.style.zoom = 1);
    }
  }
};

// uuAltCSS.ie.alphapng.undo - undo
_alphapng.undo = function(nodeArray) { // @param NodeArray:
  function removeFilter(filter) {
    var rv = [], token = uuToken.split(v.style.filter, " ", 0),
        v, i = 0;

    while ( (v = token[i++]) ) {
      if (v.indexOf(ALPHA1) < 0) {
        rv.push(v);
      }
    }
    return rv.join(" ");
  }

  var v, i = 0, hash;

  while ( (v = nodeArray[i++]) ) {
    hash = _mm.data(v, "uuAltCSS.ie.alphapng");
    if (hash.efx) {
      hash.efx = 0;
      v.src = hash.src;
      v.style.filter = removeFilter(v.style.filter);
    }
  }
};

// uuAltCSS.ie.alphapng.query - query alpha png nodes
_alphapng.query = function(context) { // @param Node: context
                                      // @return NodeArray:
  var rv = [], v, i = 0,
      ary = _query('img[src$=".png"],input[type=image][src$=".png"]',
                     context),
      idx1 = ".alpha.png", idx2 = " alpha ";

  while ( (v = ary[i++]) ) {
    if (v.src.lastIndexOf(idx1) >= 0 ||
        (" " + v.className + " ").indexOf(idx2) >= 0) {
      rv.push(v);
      _mm.data.bond(v, "uuAltCSS.ie.alphapng",
                    { efx: 0, src: v.src, w: v.width, h: v.height });
    }
  }
  return rv;
};

// === uuAltCSS.ie.disptbl ===
// care: -uu-display: table: for IE6, IE7
_disptbl = function(ary) { // @param NodeArray:
  var v, i = 0, j, tbl, row, cell;

  while ( (v = ary[i++]) ) {
    tbl = _doc.createElement("table");
    // copy attrs
    tbl.id = v.id;
    tbl.title = v.title;
    tbl.className = v.className;
    // copy style
    tbl.style.cssText = v.style.cssText;
    tbl.style.borderCollapse = "collapse";
    tbl.setAttribute("uuCSSLock", 1); // bond

    row = tbl.insertRow(); // add last row
    row.setAttribute("uuCSSLock", 1); // bond

    for (j = v.firstChild; j; j = j.nextSibling) {
      cell = row.insertCell(); // add last cell
      // copy attrs
      cell.id = j.id;
      cell.title = j.title;
      cell.className = j.className;
      // copy style
      cell.style.cssText = j.style.cssText;
      cell.style.margin = "0"; // force margin: 0
      // copy event handler(click, dblclick only)
      cell.onclick = j.onclick;
      cell.ondblclick = j.ondblclick;
      cell.setAttribute("uuCSSLock", 1); // bond

      while (j.firstChild) {
        cell.appendChild(j.removeChild(j.firstChild));
      }
    }
    v.parentNode.replaceChild(tbl, v);
  }
};

// === uuAltCSS.ie.maxmin ===
// care: max-width, min-width, max-height, min-height for IE6, IE7(td, th only)
_maxmin = function() {
  var elm, i = 0, hash,
      calcMaxWidth, calcMinWidth, calcMaxHeight, calcMinHeight,
      run, width, height, rect;

  while ( (elm = _job.maxmin[i++]) ) {
    hash = elm.uuCSSMaxMin;

    calcMaxWidth  = _maxmin.size(elm, hash, "maxWidth", 1);
    calcMinWidth  = _maxmin.size(elm, hash, "minWidth", 1);
    calcMaxHeight = _maxmin.size(elm, hash, "maxHeight");
    calcMinHeight = _maxmin.size(elm, hash, "minHeight");

    // recalc
    if (calcMaxWidth || calcMinWidth) {

      // recalc max-width
      if (calcMinWidth > calcMaxWidth) {
        calcMaxWidth = calcMinWidth;
      }

      // recalc width
      // width: auto !important
      run = elm.runtimeStyle.width;  // keep runtimeStyle.width
      elm.runtimeStyle.width = hash.orgWidth;
      elm.style.width = "auto";
      rect = elm.getBoundingClientRect(); // re-validate
      width = rect.right - rect.left;

      elm.style.width = hash.orgWidth; // o
      elm.runtimeStyle.width = run; // restore style

      // recalc limits
      if (width > calcMaxWidth) {
        width = calcMaxWidth;
        elm.style.pixelWidth = width;
      } else if (width < calcMinWidth) {
        width = calcMinWidth;
        elm.style.pixelWidth = width;
      }
    }

    if (calcMaxHeight || calcMinHeight) {

      // recalc max-height
      if (calcMinHeight > calcMaxHeight) {
        calcMaxHeight = calcMinHeight;
      }

      // recalc height
      // height: auto !important
      run = elm.runtimeStyle.height;  // keep runtimeStyle.height
      elm.runtimeStyle.height = hash.orgHeight;
      elm.style.height = "auto";
      rect = elm.getBoundingClientRect(); // re-validate
      height = rect.bottom - rect.top;

      elm.style.height = hash.orgHeight; // o
      elm.runtimeStyle.height = run; // restore style

      // recalc limits
      if (height > calcMaxHeight) {
        height = calcMaxHeight;
        elm.style.pixelHeight = height;
      } else if (height < calcMinHeight) {
        height = calcMinHeight;
        elm.style.pixelHeight = height;
      }
    }
  }
};

_mix(_maxmin, {
  size: function(elm, hash, prop, horizontal) {
    var rv = 0, rect;

    if (hash[prop] !== "") {
      if (/[\d\.]+%$/.test(hash[prop])) {
        rect = elm.parentNode.getBoundingClientRect();
        rv = _float(hash[prop]);
        rv = (horizontal ? (rect.right - rect.left)
                         : (rect.bottom - rect.top)) * rv / 100;
      } else {
        rv = _style.toPixel(elm, hash[prop], 1);
      }
    }
    return rv;
  },

  markup: function(context) {
    var rv = [], xw, nw, xh, nh,
        node = _ie6 ? _query.tag("*", context)
                    : _query("td,th", context), // IE7 td, th
        v, i = 0, cs, rex1 = /^(inherit|none|auto)$/,
        blockLevel = { block: 1, "inline-block": 1, "table-cell": 1 };

    while ( (v = node[i++]) ) {
      cs = v.currentStyle;
      if (_ie7 || blockLevel[cs.display]) {
        xw = cs["max-width"]  || cs.maxWidth || ""; // length | % | none
        nw = cs["min-width"]  || cs.minWidth || ""; // length | %
        xh = cs["max-height"] || cs.maxHeight|| ""; // length | % | none
        nh = cs["min-height"] || cs.minHeight|| ""; // length | %
                                                    // (ie6 default "auto")
        rex1.test(xw) && (xw = "");
        rex1.test(nw) && (nw = "");
        rex1.test(xh) && (xh = "");
        rex1.test(nh) && (nh = "");

        if (xw === "" && nw === "" &&
            xh === "" && nh === "") {
          if ("uuCSSMaxMin" in v) {
            delete v["uuCSSMaxMin"];
          }
          continue; // exclude
        }

        _mix(v, {
          uuCSSMaxMin: {}
        }, 0, 0);
        _mix(v.uuCSSMaxMin, {
          maxWidth: xw,
          minWidth: nw,
          maxHeight: xh,
          minHeight: nh
        });
        _mix(v.uuCSSMaxMin, {
          orgWidth:  v.currentStyle.width,
          orgHeight: v.currentStyle.height
        }, 0, 0);
        rv.push(v);
      }
    }
    _job.maxmin = rv;
  }
});

// --- initialize ---

// --- export ---
window.uuAltCSS.ie = {
  position: _position,
  alphapng: _alphapng,
  disptbl: _disptbl,
  maxmin: _maxmin
};

})();
// === uuAltCSS.calcspec ===
// depend: uuAltCSS
// http://www.w3.org/TR/css3-selectors/#specificity
(function() {
var _calcspec, // inner namespace
    SPEC_E = /\w+/g,
    SPEC_ID = /#[\w\u00C0-\uFFEE\-]+/g, // (
    SPEC_NOT = /:not\(([^\)]+)\)/,
    SPEC_ATTR =
        /\[\s*(?:([^~\^$*|=\s]+)\s*([~\^$*|]?\=)\s*(["'])?(.*?)\3|([^\]\s]+))\s*\]/g,
    SPEC_CLASS = /\.[\w\u00C0-\uFFEE\-]+/g,
    SPEC_PCLASS = /:[\w\-]+(?:\(.*\))?/g,
    SPEC_PELEMENT = /::?(?:first-letter|first-line|before|after)/g,
    SPEC_CONTAINS = /:contains\((["'])?.*?\1\)/g;

// uuAltCSS.calcspec - Calculating a selector's specificity
_calcspec = function(expr) { // @param String: simple selector(without comma)
                             // @return Number: spec value
  function A() { ++a; return ""; }
  function B() { ++b; return ""; }
  function C() { ++c; return ""; }
  function C2(m, E) { return " " + E; }

  var a = 0, b = 0, c = 0;

  expr.replace(SPEC_NOT, C2).      // :not(E)
       replace(SPEC_ID, A).        // #id
       replace(SPEC_CLASS, B).     // .class
       replace(SPEC_CONTAINS, B).  // :contains("...")
       replace(SPEC_PELEMENT, C).  // ::pseudo-element
       replace(SPEC_PCLASS, B).    // :pseudo-class
       replace(SPEC_ATTR, B).      // [attr=value]
       replace(SPEC_E, C);         // E
  // ignore the universal selector

  return a * 100 + b * 10 + c;
};

// --- initialize ---

// --- export ---
window.uuAltCSS.calcspec = _calcspec;

})(); // uuAltCSS.calcspec scope

// === uuAltCSS.cleanup ===
// depend: uuAltCSS
(function() {
var _cleanup; // inner namespace

// uuAltCSS.cleanup - Cleanup CSS
_cleanup = function(css) { // @param String: dirty css
                           // @return String: clean css
  return css.replace(/<!--|-->/g,  ""). // <!-- ... --> (
    replace(/url\(([^\)]*)\)/gi, function(m, data) { // url(...) -> url("...")
      return 'url("' + data.replace(/^["']|["']$/g, "") + '")'; // trim quote
    }).
    replace(/\\([{};,])/g, function(m, c) {
      return (0x10000 + c.charCodeAt(0)).toString(16).replace(/^1/, "\\\\u");
    }).
    replace(/@[^\{]+\{[^\}]*\}/g, ""). // @font-face @page
    replace(/@[^;]+\s*;/g, "").        // @charset
    replace(/\s*[\r\n]+\s*/g, " ").    // ...\r\n...
    replace(/[\u0000-\u001f]+/g, "").  // \u0009 -> "" (unicode)
    replace(/\\x[01]?[0-9a-f]/gi, ""). // "\x9"  -> "" (hex \x00~\1f)
    replace(/\\[0-3]?[0-7]/g, "").     // "\9"   -> "" (octet \0~\37)
    replace(/^\s+|\s+$/g, "");         // trim space
};

// --- initialize ---

// --- export ---
window.uuAltCSS.cleanup = _cleanup;

})(); // uuAltCSS.cleanup scope

// === uuAltCSS.imports ===
// depend: uuMeta, uuAltCSS, uuCodec
(function() {
var _imports, // inner namespace
    _mm = uuMeta,
    _ie = _mm.ie,
    _win = window,
    _doc = document,
    _xhr, // lazy
    _cache = {}, // import cache { url: cssText }
    _codec = ((_win.UUALTCSS_DECODE_DATAURI || 0)
                && _win.uuCodecDataURI) ? uuCodecDataURI : 0,
    DATA_SCHEME_CSS = /^data\:text\/css[;,]/,
    MEMENTO = "uuAltCSSMemento",
    COMMENT = /\/\*[^*]*\*+([^\/][^*]*\*+)*\//g,
    IMPORTS =
        /@import\s*(?:url)?[\("']+\s*([\w\.\/\+\-]+)\s*["'\)]+\s*([\w]+)?\s*;/g;

// uuAltCSS._imports - Import CSS
_imports = function() { // @return String: minified CSS
  function imports(css, absdir) { // @import
    return css.replace(COMMENT, "").
               replace(IMPORTS, function(m, url, media) {
      var v = toAbsURL(url, absdir);
      return imports(sync(v), toDir(v));
    });
  }

  var rv = [], absdir = toAbsURL("."), href, hash, dstr,
      node = _mm.toArray(_doc.styleSheets), v, w, i = 0,
      prop1 = _ie ? "owningElement" : "ownerNode",
      prop2 = _ie ? MEMENTO : "textContent";

  while ( (v = node[i++]) ) {
    if (!v.disabled) {
      href = v.href || "";
      if (!DATA_SCHEME_CSS.test(href)) { // ignore data:text/css for !(IE6,IE7)
        if (/\.css$/.test(href)) {
          // <link>
          w = toAbsURL(v.href, absdir);
          !(w in _cache) && (_cache[w] = imports(sync(w), toDir(w)));
          rv.push(_cache[w]);
        } else {
          // <style>
          rv.push(imports(v[prop1][prop2], absdir));
        }
      }
    }
  }
  // decode datauri
  //    <link href="data:text/css,...">
  if (_codec) {
    node = _doc.getElementsByTagName("link"), i = 0;
    while ( (v = node[i++]) ) {
      if (DATA_SCHEME_CSS.test(v.href)) {
        hash = _codec.decode(v.href);
        dstr = String.fromCharCode.apply(null, hash.data);
        w = "link" + i; // "link1"
        !(w in _cache) && (_cache[w] = imports(dstr, absdir));
        rv.push(_cache[w]);
      }
    }
  }
  return rv.join("");
};

function toAbsURL(url, curtdir) {
  if (!/^(file|https|http)\:/.test(url)) {
    var div = _doc.createElement("div");

    div.innerHTML = '<a href="' + (curtdir || "") + url + '" />';
    url = div.firstChild ? div.firstChild.href
                         : /href\="([^"]+)"/.exec(div.innerHTML)[1];
  }
  return url;
}

function toDir(absurl) {
  var ary = absurl.split("/");
  ary.pop();
  return ary.join("/") + "/";
}

function sync(url) {
  try {
    if (!_xhr && _ie && "ActiveXObject" in _win) {
      _xhr = new ActiveXObject("Microsoft.XMLHTTP");
    }
    if (!_xhr && "XMLHttpRequest" in _win) {
      _xhr = new XMLHttpRequest();
    }
    if (_xhr) {
      _xhr.open("GET", url, false);
      _xhr.send(null);
      return (_xhr.status === 200 || !_xhr.status) ? _xhr.responseText : "";
    }
  } catch (err) {} // ignore cross domain request
  return "";
}

// --- initialize ---

// --- export ---
_win.uuAltCSS.imports = _imports;

})(); // uuAltCSS.imports scope

// === uuAltCSS.boxeffect ===
// depend: uuAltCSS
(function() {
var _boxeffect, // inner namespace
    _altcss = uuAltCSS,
    _setex = _altcss.setExStyle,
    _mm = uuMeta,
    _ie = _mm.ie,
    _ie6 = _ie && _mm.uaver === 6,
    _ie7 = _ie && _mm.uaver === 7,
    _ie67 = _ie6 || _ie7,
    _style = uuStyle,
    _color = uuColor,
    _validator = uuCSSValidator,
    _toPixel = _style.toPixel,
    _runstyle = _mm.runstyle,
    _uids = {}, // { uid: node, ... }
    _float = parseFloat,
    TRANSPARENT = "transparent",
    TRIM = /^\s+|\s+$/g,
    MBG = /-uu-background(-image|-repeat|-position|-color|-attachment|-clip|-origin|-size|-break)?/g,
    MBG_CASE = { "-image": 2, "-repeat": 2, "-position": 2, "-color": 2 },
    BTW = "borderTopWidth",
    BLW = "borderLeftWidth",
    BRW = "borderRightWidth",
    BBW = "borderBottomWidth",
    BTC = "borderTopColor";

// uuAltCSS.boxeffect
_boxeffect = function(node, excss) {
  var view = node.parentNode,
      vs = _ie ? view[_runstyle] : _runstyle(view, ""), // view currentStyle
      ns = _ie ? node[_runstyle] : _runstyle(node, ""), // node currentStyle
      uid = _mm.uid(node), m, mbgsh = 0,
      bfx, hash, canvas,
      decl, declw, declh;

  if (vs.display === "none" || ns.display === "none") {
    return;
  }

  _uids[uid] = node;

  _ie67 && fixIELayoutBug(view, node, vs, ns);

  bfx = _boxeffect.bond(node, excss);

  _setex(node, "-uu-box-effect",    bfx.decl["-uu-box-effect"] || "auto");
  _setex(node, "-uu-box-shadow",    bfx.decl["-uu-box-shadow"]);
  _setex(node, "-uu-box-reflect",   bfx.decl["-uu-box-reflect"]);
  _setex(node, "-uu-border-radius", bfx.decl["-uu-border-radius"]);
  trainMargin(bfx);
  trainBorder(bfx);
  _ie67 && trainFakeBorder(bfx);

  while ( (m = MBG.exec(bfx.order)) ) {
    if ((!m[1] && !mbgsh++) || MBG_CASE[m[1]]) {
      _setex(node, m[0], bfx.decl[m[0]]);
    }
  }
  trainMBG(bfx);

  if (bfx.boxreflect.render) {
    hash = bfx.boxreflect;
    if (hash.dir === "below") {
      if (node.tagName === "IMG") {
        // delay loader
        uuImage.load(node.src, function(img, state, dim) {
          if (state === 1) {
            bfx.layer = new uuLayer(view, dim.w, dim.h * 2 + hash.offset);
            bfx.layer.createReflectionLayer(
                "reflect", node, 0,
                node.offsetLeft, node.offsetTop, 0, 0,
                void 0, 0, hash.offset);
            node.style.visibility = "hidden";
            bfx.hasReflectLayer = 1;
          }
          img.clear();
        });
      }
    }
  } else {
    canvas = "vmlcanvas";
    if (_ie && _mm.slver > 0 &&
        bfx.decl["-uu-canvas-type"] === "sl") {
      bfx.slmode = 1; // Silverlight mode(buggy)
      canvas = "canvas";
    }

    // get declaration value("auto" or "200px"),
    // The computed value cannot be used.
    declw = declh = "auto";
    decl = _altcss.getDeclaredValues(view);
    if (!decl) {
      decl = { order: "" };
    }

    if (view.style.width && view.style.width !== "auto") {
      declw = view.style.width;
    } else if (decl.order.indexOf("width,") >= 0) {
      declw = decl.decl.width;
    }
    if (view.style.height && view.style.height !== "auto") {
      declh = view.style.height;
    } else if (decl.order.indexOf("height,") >= 0) {
      declh = decl.decl.height;
    }

    bfx.layer = new uuLayer(view, declw, declh);
    bfx.nodebgLayer = bfx.layer.createLayer("nodebg", canvas, 0, 1,
                                            bfx.nodeRect.ow, bfx.nodeRect.oh);
    bfx.viewbgLayer = bfx.layer.createLayer("viewbg", canvas, 0, 1,
                                            bfx.viewRect.ow, bfx.viewRect.oh);
    bfx.layer.appendLayer("node", node);
  }
  bfx.nodeOffset = getOffsetFromAncestor(bfx.node, view);
  if (bfx.slmode) {
    uuCanvas.ready(function() {
      boxeffectDraw(bfx, 0);
    });
  } else {
    boxeffectDraw(bfx, 0);
  }
};

// uuAltCSS.boxeffect.bond
_boxeffect.bond = function(node,    // @param Node:
                           excss) { // @param excss:
                                    // @return Hash:
  if (!("uuAltCSSBoxEffect" in node)) {
    var view = node.parentNode;

    node.uuAltCSSBoxEffect = {
      decl:         excss.decl,
      order:        excss.order,
      view:         view,
      node:         node,
      layer:        0,
      nodebgLayer:  0,
      viewbgLayer:  0,
      hasReflectLayer: 0,
      slmode:       0, // 1 = Silverlight mode
      viewRect:     _style.getRect(view),
      nodeRect:     _style.getRect(node),
      nodeOffset:   0, // lazy
      train:        { margin: 0, border: 0, mbg: 0 }, // 1: retrain
      margin:       { t: 0, l: 0, r: 0, b: 0 },
      border:       { render: 0, shorthand: 0,
                      t: 0, l: 0, r: 0, b: 0,
                      tc: ["#000000", 0] },
      mbg:          { render: 0,
                      type: [],
                      image: ["none"],
                      repeat: ["repeat"],
                      position: ["0% 0%"],
                      attachment: ["scroll"],
                      origin: ["padding"],
                      clip: ["no-clip"],
                      rgba: { r: 0, g: 0, b: 0, a: 0 },
                      altcolor: _style.getBGColor(node, 1, 1),
                      grad: [],
                      imgobj: [],
                      tid: -1 },
      bradius:      { render: 0, shorthand: 0, r: [0, 0, 0, 0] },
      boxshadow:    { render: 0, rgba: 0, ox: 0, oy: 0, blur: 0 },
      boxreflect:   { render: 0, dir: 0, offset: 0, url: 0,
                      grad: { render: 0 } },
      boxeffect:    { render: 0 }
    };
  }
  return node.uuAltCSSBoxEffect;
};

// uuAltCSS.boxeffect.recalc
_boxeffect.recalc = function() {
  var uid, view, bfx, vs, ns;

  for (uid in _uids) {
    node = _uids[uid];
    view = node.parentNode;

    vs = _ie ? view[_runstyle] : _runstyle(view, ""); // view currentStyle
    ns = _ie ? node[_runstyle] : _runstyle(node, ""); // node currentStyle

    if (vs.display === "none" || ns.display === "none") {
      continue;
    }

    bfx = node.uuAltCSSBoxEffect;
    boxeffectRecalcRect(node, bfx);
    // improvement of response time
    (function(arg) {
      setTimeout(function() {
        boxeffectDraw(arg, 1);
      }, 0);
    })(bfx);
  }
};

function train(bfx) {
  if (bfx.train.margin || bfx.train.border) {
    bfx.train.margin && trainMargin(bfx);
    bfx.train.border && trainBorder(bfx);
    _ie67 && trainFakeBorder(bfx);
    bfx.train.margin = 0;
    bfx.train.border = 0;
  }
  if (bfx.train.mbg) {
    trainMBG(bfx);
    bfx.train.mbg = 0;
  }
}

function boxeffectRecalcRect(node, bfx) {
  train(bfx);

  // http://d.hatena.ne.jp/uupaa/20090719
  if (_ie67) { // restore border and margin state
    _mm.mix(node.style, bfx.ie6borderorg);
  }
  // update rect
  bfx.nodeRect = _style.getRect(node);
  bfx.viewRect = _style.getRect(bfx.view);

  bfx.nodeOffset = getOffsetFromAncestor(bfx.node, bfx.view);

  if (_ie67) {
    _mm.mix(node.style, bfx.ie6borderfix);
  }
}

function boxeffectDraw(bfx, redraw) {
  var node = bfx.node,
//    view = bfx.view,
      layer = bfx.layer,
      nodebg = bfx.nodebgLayer,
      viewbg = bfx.viewbgLayer,
      nctx,
      vctx,
      hash, ary;

  train(bfx);

  if (layer) {
    nctx = layer.getContext("nodebg");
    vctx = layer.getContext("viewbg");
  }

  if (0) { // debug
    layer.view.style.border = "2px solid pink";
    nodebg.style.border = "5px solid red";
    viewbg.style.border = "5px solid green";
  }

  if (redraw) {
    viewbg && layer.resizeLayer("viewbg", bfx.viewRect.w, bfx.viewRect.h).
              push("viewbg").clear().pop();
    nodebg && layer.resizeLayer("nodebg", bfx.nodeRect[_ie67 ? "ow" : "w"],
                                          bfx.nodeRect[_ie67 ? "oh" : "h"]).
              push("nodebg").clear().pop();
  }

  // CSS3 background-origin:
  if (nodebg) {
    nodebg.style.left =
        (bfx.nodeOffset.x + (_ie67 ? 0 : bfx.border.l)) + "px";
    nodebg.style.top =
        (bfx.nodeOffset.y + (_ie67 ? 0 : bfx.border.t)) + "px";
  }

  if (viewbg) {
    // ToDo: clipping path for background-color: rgba(,,,0.5) support
/* keep
    if (0) {
      vctx.save();
      vctx.rect(0, 0, bfx.viewRect.w, bfx.viewRect.h);
      boxpath(vctx,
        bfx.nodeOffset.x + bfx.border.l,
        bfx.nodeOffset.y + bfx.border.t,
                bfx.nodeRect.ow - bfx.border.l - bfx.border.r,
                bfx.nodeRect.oh - bfx.border.t - bfx.border.b,
                bfx.bradius.r,
                1); // open path
      vctx.clip();
    }
 */
    // draw shadow
    if (bfx.boxeffect.render && bfx.boxshadow.render) {
      hash = bfx.boxshadow;
      vctx.save();
      drawFakeShadow(vctx,
                     bfx.nodeOffset.x - hash.blur / 2 + hash.ox,
                     bfx.nodeOffset.y - hash.blur / 2 + hash.oy,
                     bfx.nodeRect.ow + hash.blur,
                     bfx.nodeRect.oh + hash.blur,
                     hash.rgba,
                     Math.max(hash.blur, Math.abs(hash.ox * 2),
                                         Math.abs(hash.oy * 2)),
                     bfx.bradius.r);
      vctx.restore();
    }

    // draw border
    if (bfx.boxeffect.render && bfx.border.render) {
      ary = [];
      if (bfx.boxshadow.render) {
        hash = bfx.bradius.r;
        ary[0] = !hash[0] ? 1 : (hash[0] < 40) ? hash[0] + 4 : hash[0];
        ary[1] = !hash[1] ? 1 : (hash[1] < 40) ? hash[1] + 4 : hash[1];
        ary[2] = !hash[2] ? 1 : (hash[2] < 40) ? hash[2] + 4 : hash[2];
        ary[3] = !hash[3] ? 1 : (hash[3] < 40) ? hash[3] + 4 : hash[3];
      } else {
        ary = bfx.bradius.r;
      }
      vctx.save();
      vctx.fillStyle = bfx.border.tc[0];
      boxpath(vctx,
              bfx.nodeOffset.x,
              bfx.nodeOffset.y,
              bfx.nodeRect.ow,
              bfx.nodeRect.oh,
              ary);
      vctx.fill();
      vctx.restore();
    }
/* keep
    if (0) { // end clip
      vctx.restore();
    }
 */
  }

  if (nodebg) {
    layer.push("nodebg");

    // draw background-color
    if (bfx.boxeffect.render) {
      if (bfx.border.render ||
          bfx.boxshadow.render ||
          bfx.mbg.rgba.a) {
        nctx.save();
        if (!bfx.mbg.rgba.r &&
            !bfx.mbg.rgba.g &&
            !bfx.mbg.rgba.b &&
            !bfx.mbg.rgba.a) { // -uu-background-color: transparent
          nctx.globalAlpha = bfx.mbg.altcolor.a;
          nctx.fillStyle = _color.hex(bfx.mbg.altcolor);
        } else {
          nctx.globalAlpha = bfx.mbg.rgba.a;
          nctx.fillStyle = _color.hex(bfx.mbg.rgba);
        }
        boxpath(nctx,
                _ie67 ? bfx.border.l : 0,
                _ie67 ? bfx.border.t : 0,
                bfx.nodeRect.ow - bfx.border.l - bfx.border.r,
                bfx.nodeRect.oh - bfx.border.t - bfx.border.b,
                bfx.bradius.r);
        nctx.fill();
        nctx.restore();
      }
    }

    // draw multiple background image
    if (bfx.boxeffect.render) {
      nctx.save();
      drawMultipleBackgroundImage(bfx, nctx);
      nctx.restore();
    }

    // rewrite border, clipping mbg
    if (_ie67) {
      if (bfx.border.render && bfx.border.shorthand) {
        if (!bfx.bradius.r[0] && bfx.bradius.shorthand) {
          nctx.save();
          nctx.strokeStyle = bfx.border.tc[0];
          nctx.lineWidth = bfx.border.t * 2;
          boxpath(nctx, 0, 0,
                  bfx.nodeRect.ow,
                  bfx.nodeRect.oh,
                  bfx.bradius.r);
          nctx.stroke();
          nctx.restore();
        }
      }
    }

    layer.pop();
  }

  // bg setting
  node.style.backgroundColor = TRANSPARENT;
  node.style.backgroundImage = "none";
  node.style.borderColor = TRANSPARENT;

  // http://d.hatena.ne.jp/uupaa/20090719
  // IE6 'borderColor = "transparent";' unsupported
  if (_ie67) {
    _mm.mix(node.style, bfx.ie6borderfix);
  }

  if (!redraw && bfx.slmode) {
    _altcss.deny = 1;
  }
}

function drawFakeShadow(ctx, x, y, width, height,
                        rgba, blur, radius) {
  var i = 0, j = 0, k, step = 1, line = 5, r = radius,
      fg = "rgba(" + [rgba.r, rgba.g, rgba.b, ""].join(","); // fragment

  if (blur > 30 || (_ie6 && !_mm.slver)) { // IE6 + VML - cut short
    step *= 2, line *= 2;
  }
  ctx.globalAlpha = 1;
  ctx.lineWidth = line;
  for (; i < blur; i += step) {
    k = i / blur;
    j += 0.5;
    ctx.strokeStyle = fg + (k * k * k) + ")";
    boxpath(ctx, x + i, y + i, width - (i * 2), height - (i * 2),
            [r[0] - j, r[1] - j, r[2] - j, r[3] - j]);
    ctx.stroke();
  }
}

function drawMultipleBackgroundImage(bfx, ctx) {
  var mbg = bfx.mbg, i = 0, iz = bfx.mbg.image.length,
      img, draw = 0, pos = [];

  for (; i < iz; ++i) {
    switch (mbg.type[i]) {
    case 0: // -uu-background-image: none
      break;
    case 1: // image
      img = mbg.imgobj[i];
      if (img.state === 2) {
        pos[i] = trainBackgroundPosition(bfx, mbg.position[i], img);
        ++draw;
      }
      break;
    case 2: // gradient
      if (mbg.grad[i].render) {
        ++draw;
      }
    }
  }

  if (draw) {
    if (!_ie || bfx.slmode) {
      // http://d.hatena.ne.jp/uupaa/20090815/1250330291
      // Google Chrome3 HTML5::Canvas.clip Jaggies
      if (bfx.border.render) {
        boxpath(ctx,
                _ie67 ? bfx.border.l : -1,
                _ie67 ? bfx.border.t : -1,
                bfx.nodeRect.ow - bfx.border.l - bfx.border.r + 2,
                bfx.nodeRect.oh - bfx.border.t - bfx.border.b + 2,
                bfx.bradius.r);
      } else {
        boxpath(ctx,
                _ie67 ? bfx.border.l : 0,
                _ie67 ? bfx.border.t : 0,
                bfx.nodeRect.ow - bfx.border.l - bfx.border.r,
                bfx.nodeRect.oh - bfx.border.t - bfx.border.b,
                bfx.bradius.r);
      }
      ctx.clip();
    }
    while (i--) {
      switch (mbg.type[i]) {
      case 1:
        img = mbg.imgobj[i];
        if (img.state === 2) {
          switch (mbg.repeat[i]) {
          case "no-repeat":
            // http://twitter.com/uupaa/status/2763996863
            // Firefox2 bugfix
            ctx.drawImage(img, pos[i].x | 0, pos[i].y | 0);
            break;
          case "repeat-x":
          case "repeat-y":
            drawImageTile(bfx, ctx, img,
                          (mbg.repeat[i] === "repeat-x" ? 1 : 0),
                          pos[i].x | 0, pos[i].y | 0,
                          _ie67 ? bfx.border.l : 0,
                          _ie67 ? bfx.border.t : 0,
                          bfx.nodeRect.ow - bfx.border.l - bfx.border.r,
                          bfx.nodeRect.oh - bfx.border.t - bfx.border.b);
            break;
          case "repeat":
          default:
            ctx.save();
            ctx.fillStyle = ctx.createPattern(img, "repeat");
            boxpath(ctx,
                    _ie67 ? bfx.border.l : 0,
                    _ie67 ? bfx.border.t : 0,
                    bfx.nodeRect.ow - bfx.border.l - bfx.border.r,
                    bfx.nodeRect.oh - bfx.border.t - bfx.border.b,
                    bfx.bradius.r);
            ctx.fill();
            ctx.restore();
            break;
          }
        }
        break;
      case 2:
        if (mbg.grad[i].render) {
          drawGradient(bfx, ctx, mbg.grad[i]);
        }
      }
    }
  }
}

function drawImageTile(bfx, ctx, img, horizontal,
                       ix, iy, left, top, right, bottom) {
  var x = ix, y = iy, w = img.width, h = img.height,
      xmin = left - w, ymin = top - h;

  if (horizontal) {
    for (; x < right; x += w) {
      ctx.drawImage(img, x, y);
    }
    for (x = ix - w; x > xmin; x -= w) {
      ctx.drawImage(img, x, y);
    }
  } else {
    for (; y < bottom; y += h) {
      ctx.drawImage(img, x, y);
    }
    for (y = iy - h; y > ymin; y -= h) {
      ctx.drawImage(img, x, y);
    }
  }
}

function drawGradient(bfx, ctx, hash) {
  function pos(str, size) {
    return /%$/.test(str) ? (size * _float(str) / 100)
                          : _float(str);
  }

  var p0 = pos(hash.point[0], bfx.nodeRect.ow),
      p1 = pos(hash.point[1], bfx.nodeRect.oh),
      p2 = pos(hash.point[2], bfx.nodeRect.ow),
      p3 = pos(hash.point[3], bfx.nodeRect.oh);

  ctx.save();
  ctx.fillStyle = (hash.type === "linear")
      ? bfx.layer.linearGrad(p0, p1, p2, p3,
                              hash.offset, hash.color)
      : bfx.layer.radialGrad(p0, p1, hash.radius[0],
                              p2, p3, hash.radius[1],
                              hash.offset, hash.color);
  boxpath(ctx,
          _ie67 ? bfx.border.l : 0,
          _ie67 ? bfx.border.t : 0,
          bfx.nodeRect.ow - bfx.border.l - bfx.border.r,
          bfx.nodeRect.oh - bfx.border.t - bfx.border.b,
          bfx.bradius.r);
  ctx.fill();
  ctx.restore();
}

function trainMBG(bfx) {
  var mbg = bfx.mbg, i = 0, iz, m, url, N;

  // spec http://www.w3.org/TR/css3-background/#layering
  N = Math.max(mbg.image.length, mbg.repeat.length, mbg.position.length);

  if (N > mbg.image.length) {
    mbg.image = multipleArray(mbg.image,
                              Math.ceil(N / mbg.image.length), N);
  }
  if (N > mbg.repeat.length) {
    mbg.repeat = multipleArray(mbg.repeat,
                               Math.ceil(N / mbg.repeat.length), N);
  }
  if (N > mbg.position.length) {
    mbg.position = multipleArray(mbg.position,
                                 Math.ceil(N / mbg.position.length), N);
  }

  for (iz = mbg.image.length; i < iz; ++i) {
    mbg.image[i] = mbg.image[i].replace(TRIM, ""); // trim both
    mbg.repeat[i] = mbg.repeat[i].replace(TRIM, "");
    mbg.position[i] = mbg.position[i].replace(TRIM, "");
    mbg.type[i] = 0; // unknown

    if ( (m = /^\s*url\((.*)\)$/.exec(mbg.image[i])) ) {
      mbg.type[i] = 1; // image
      url = m[1].replace(/^\s*[\"\']?|[\"\']?\s*$/g, "");
    } else if (/^\s*-uu-gradient/.test(mbg.image[i])) {
      mbg.type[i] = 2; // gradient
      mbg.grad[i] = _validator.gradient(mbg.image[i]);
      mbg.grad[i].render = (mbg.grad[i].valid &&
                            mbg.grad[i].type) ? 1 : 0;
      continue;
    } else {
      continue; // "none" too
    }
    mbg.imgobj[i] = new Image();
    mbg.imgobj[i].state = 0; // bond
    // lazy load
    (function(imgobj, url) {
      imgobj.onerror = function() {
        imgobj.state = 1;
      };
      imgobj.onload = function() {
        if (imgobj.complete ||
            imgobj.readyState === "complete") { // IE8
          imgobj.state = 2;
          if (bfx.mbg.tid >= 0) {
            clearTimeout(bfx.mbg.tid);
          }
          bfx.mbg.tid = setTimeout(function() {
            boxeffectRecalcRect(bfx.node, bfx);
            boxeffectDraw(bfx, 1)
            bfx.mbg.tid = -1;
          }, 100);
        }
      };
      imgobj.setAttribute("src", url);
    })(mbg.imgobj[i], url);
  }
}

function trainBackgroundPosition(bfx, pos, img) {
  var key1 = { left: "0%", center: "50%", right: "100%" },
      key2 = { top: "0%", center: "50%", bottom: "100%" },
      ary, px, py,
      nw = bfx.nodeRect[_ie67 ? "ow" : "w"],
      nh = bfx.nodeRect[_ie67 ? "oh" : "h"],
      iw = img.width,
      ih = img.height;

  ary = (pos.indexOf(" ") > 0) ? pos.split(" ")
                               : [pos, pos];

  if (ary[0] === "top" || ary[0] === "bottom" ||
      ary[1] === "left" || ary[1] === "right") {
    ary.reverse(); // "top left" -> "left top"
  }

  ary[0] = key1[ary[0]] || ary[0];
  ary[1] = key2[ary[1]] || ary[1];

  if (ary[0].lastIndexOf("%") > 0) {
    px = nw * _float(ary[0]) / 100
       - iw * _float(ary[0]) / 100;
  } else {
    px = _style.toPixel(bfx.node, ary[0]);
    if (_ie67) {
      px += bfx.border.l;
    }
  }

  if (ary[1].lastIndexOf("%") > 0) {
    py = nh * _float(ary[1]) / 100
       - ih * _float(ary[1]) / 100;
  } else {
    py = _style.toPixel(bfx.node, ary[1]);
    if (_ie67) {
      py += bfx.border.t;
    }
  }
  return { x: px, y: py };
}

function boxpath(ctx, x, y, w, h, rary, openPath) {
  var r0 = rary[0], r1 = rary[1], r2 = rary[2], r3 = rary[3],
      w2 = (w / 2) | 0, h2 = (h / 2) | 0;

  if (r0 < 0) { r0 = 0; }
  if (r1 < 0) { r1 = 0; }
  if (r2 < 0) { r2 = 0; }
  if (r3 < 0) { r3 = 0; }
  if (r0 >= w2 || r0 >= h2) { r0 = Math.min(w2, h2) - 2; }
  if (r1 >= w2 || r1 >= h2) { r1 = Math.min(w2, h2) - 2; }
  if (r2 >= w2 || r2 >= h2) { r2 = Math.min(w2, h2) - 2; }
  if (r3 >= w2 || r3 >= h2) { r3 = Math.min(w2, h2) - 2; }

  if (!openPath) {
    ctx.beginPath();
  }
  ctx.moveTo(x, y + h2);
  ctx.lineTo(x, y + h - r3);
  ctx.quadraticCurveTo(x, y + h, x + r3, y + h); // bottom-left
  ctx.lineTo(x + w - r2, y + h);
  ctx.quadraticCurveTo(x + w, y + h, x + w, y + h - r2); // bottom-right
  ctx.lineTo(x + w, y + r1);
  ctx.quadraticCurveTo(x + w, y, x + w - r1, y); // top-left
  ctx.lineTo(x + r0, y);
  ctx.quadraticCurveTo(x, y, x, y + r0); // top-right
  ctx.closePath();
}

function getOffsetFromAncestor(elm, ancestor) {
  var e = elm, x = 0, y = 0;

  while (e && e !== ancestor) {
    x += e.offsetLeft || 0;
    y += e.offsetTop  || 0;
    e  = e.offsetParent;
  }
  return { x: x, y: y };
}

function multipleArray(ary, times, maxLength) {
  var rv = [], i = 0, iz;

  for (; i < times; ++i) {
    rv = rv.concat(ary);
  }
  if (rv.length > maxLength) {
    for (i = 0, iz = rv.length - maxLength; i < iz; ++i) {
      rv.pop();
    }
  }
  return rv;
}

function trainMargin(bfx) {
  var node = bfx.node,
      hash = bfx.margin,
      ns = _ie ? node[_runstyle] : _runstyle(node, "");

  hash.t = _toPixel(node, ns.marginTop);
  hash.l = _toPixel(node, ns.marginLeft);
  hash.r = _toPixel(node, ns.marginRight);
  hash.b = _toPixel(node, ns.marginBottom);
}

function trainBorder(bfx) {
  var node = bfx.node,
      hash = bfx.border,
      ns = _ie ? node[_runstyle] : _runstyle(node, "");

  hash.render = 0;
  hash.shorthand = 0;
  hash.t = _toPixel(node, ns[BTW]);
  hash.l = _toPixel(node, ns[BLW]);
  hash.r = _toPixel(node, ns[BRW]);
  hash.b = _toPixel(node, ns[BBW]);
  hash.tc = _color.parse(ns[BTC]);

  if (hash.tc[1]) { // has border
    if (hash.t || hash.l || hash.r || hash.b) {
      hash.render = 1;
    }
  }
  if (hash.t === hash.r &&
      hash.t === hash.b &&
      hash.t === hash.l) {
    hash.shorthand = 1;
  }
}

function trainFakeBorder(bfx) {
  // http://d.hatena.ne.jp/uupaa/20090719
  var node = bfx.node,
      ns = _ie ? node[_runstyle] : _runstyle(node, "");

  bfx.ie6borderorg = {
    marginTop: ns.marginTop,
    marginLeft: ns.marginLeft,
    marginRight: ns.marginRight,
    marginBottom: ns.marginBottom,
    borderTopWidth: ns[BTW],
    borderLeftWidth: ns[BLW],
    borderRightWidth: ns[BRW],
    borderBottomWidth: ns[BBW],
    borderStyle: "solid"
  };
  bfx.ie6borderfix = {
    marginTop: (bfx.margin.t + bfx.border.t) + "px",
    marginLeft: (bfx.margin.l + bfx.border.l) + "px",
    marginRight: (bfx.margin.r + bfx.border.r) + "px",
    marginBottom: (bfx.margin.b + bfx.border.b) + "px",
    border: "0px none"
  };
}

// IE6,IE7 CSS layout bugfix
function fixIELayoutBug(view, node, viewStyle, nodeStyle) { // IE6, IE7
  if (!viewStyle.hasLayout) {
    view.style.zoom = 1;
  }
  node.style.zoom = 1; // apply z-index(sink canvas)
  if (nodeStyle.position === "static") {
    node.style.position = "relative"; // set "position: relative"
  }
  // bugfix position:relative + margin:auto
  // see demo/viewbox_position/position_relative.htm
  if (node.style.position === "relative") {
    (nodeStyle.marginTop === "auto") && (node.style.marginTop = 0);
    (nodeStyle.marginLeft === "auto") && (node.style.marginLeft = 0);
    (nodeStyle.marginRight === "auto") && (node.style.marginRight = 0);
    (nodeStyle.marginBottom === "auto") && (node.style.marginBottom = 0);
  }
}

// --- initialize ---

// --- export ---
_altcss.boxeffect = _boxeffect;

})(); // uuAltCSS.boxeffect scope

